1 // -----------------------------------------------------------------------
\r
2 // Copyright (c) Microsoft Corporation. All rights reserved.
\r
3 // -----------------------------------------------------------------------
\r
5 using System.Collections.Generic;
\r
6 using System.ComponentModel.Composition.Diagnostics;
\r
7 using System.ComponentModel.Composition.Hosting;
\r
8 using System.ComponentModel.Composition.Primitives;
\r
9 using System.ComponentModel.Composition.ReflectionModel;
\r
11 using System.Reflection;
\r
12 using Microsoft.Internal;
\r
13 using Microsoft.Internal.Collections;
\r
15 namespace System.ComponentModel.Composition.AttributedModel
\r
17 internal class AttributedPartCreationInfo : IReflectionPartCreationInfo
\r
19 private readonly Type _type;
\r
20 private readonly bool _ignoreConstructorImports = false;
\r
21 private readonly ICompositionElement _origin;
\r
22 private PartCreationPolicyAttribute _partCreationPolicy = null;
\r
23 private ConstructorInfo _constructor;
\r
24 private IEnumerable<ExportDefinition> _exports;
\r
25 private IEnumerable<ImportDefinition> _imports;
\r
26 private HashSet<string> _contractNamesOnNonInterfaces;
\r
28 public AttributedPartCreationInfo(Type type, PartCreationPolicyAttribute partCreationPolicy, bool ignoreConstructorImports, ICompositionElement origin)
\r
30 Assumes.NotNull(type);
\r
32 this._ignoreConstructorImports = ignoreConstructorImports;
\r
33 this._partCreationPolicy = partCreationPolicy;
\r
34 this._origin = origin;
\r
37 public Type GetPartType()
\r
42 public Lazy<Type> GetLazyPartType()
\r
44 return new Lazy<Type>(this.GetPartType, false);
\r
47 public ConstructorInfo GetConstructor()
\r
49 if (this._constructor == null && !this._ignoreConstructorImports)
\r
51 this._constructor = SelectPartConstructor(this._type);
\r
53 return this._constructor;
\r
56 public IDictionary<string, object> GetMetadata()
\r
58 return this._type.GetPartMetadataForType(this.CreationPolicy);
\r
61 public IEnumerable<ExportDefinition> GetExports()
\r
63 DiscoverExportsAndImports();
\r
64 return this._exports;
\r
67 public IEnumerable<ImportDefinition> GetImports()
\r
69 DiscoverExportsAndImports();
\r
70 return this._imports;
\r
73 public bool IsDisposalRequired
\r
77 return typeof(IDisposable).IsAssignableFrom(this.GetPartType());
\r
81 public bool IsPartDiscoverable()
\r
83 if (this._type.IsAttributeDefined<PartNotDiscoverableAttribute>())
\r
85 CompositionTrace.DefinitionMarkedWithPartNotDiscoverableAttribute(this._type);
\r
89 if (this._type.ContainsGenericParameters)
\r
91 CompositionTrace.DefinitionContainsGenericsParameters(this._type);
\r
97 CompositionTrace.DefinitionContainsNoExports(this._type);
\r
104 private bool HasExports()
\r
106 return GetExportMembers(this._type).Any() ||
\r
107 GetInheritedExports(this._type).Any();
\r
110 string ICompositionElement.DisplayName
\r
112 get { return this.GetDisplayName(); }
\r
115 ICompositionElement ICompositionElement.Origin
\r
117 get { return this._origin; }
\r
120 public override string ToString()
\r
122 return GetDisplayName();
\r
125 private string GetDisplayName()
\r
127 return this.GetPartType().GetDisplayName();
\r
130 private CreationPolicy CreationPolicy
\r
134 if (this._partCreationPolicy == null)
\r
136 this._partCreationPolicy = this._type.GetFirstAttribute<PartCreationPolicyAttribute>() ?? PartCreationPolicyAttribute.Default;
\r
138 return this._partCreationPolicy.CreationPolicy;
\r
142 private static ConstructorInfo SelectPartConstructor(Type type)
\r
144 Assumes.NotNull(type);
\r
146 if (type.IsAbstract)
\r
151 // Only deal with non-static constructors
\r
152 BindingFlags flags = BindingFlags.Public | BindingFlags.NonPublic | BindingFlags.Instance;
\r
154 ConstructorInfo[] constructors = type.GetConstructors(flags);
\r
156 // Should likely only happen for static or abstract types
\r
157 if (constructors.Length == 0)
\r
162 // Optimize single default constructor.
\r
163 if (constructors.Length == 1 && constructors[0].GetParameters().Length == 0)
\r
165 return constructors[0];
\r
168 // Select the marked constructor if there is exactly one marked
\r
169 IEnumerable<ConstructorInfo> importingConstructors = constructors.Where(
\r
170 ctor => ctor.IsAttributeDefined<ImportingConstructorAttribute>());
\r
172 switch (importingConstructors.GetCardinality())
\r
174 case EnumerableCardinality.One:
\r
176 return importingConstructors.First();
\r
179 case EnumerableCardinality.TwoOrMore:
\r
181 // Return null, the part will error on instantiation.
\r
186 // If there are no marked constructors then select the default constructor
\r
187 IEnumerable<ConstructorInfo> defaultConstructors = constructors.Where(
\r
188 ctor => ctor.GetParameters().Length == 0);
\r
190 // There should only ever be zero or one default constructors
\r
191 return defaultConstructors.SingleOrDefault();
\r
194 private void DiscoverExportsAndImports()
\r
196 // NOTE : in most cases both of these will be null or not null at the same time
\r
197 // the only situation when that is not the case is when there was a failure during the previous discovery
\r
198 // and one of them ended up not being set. In that case we will force the discovery again so that the same exception is thrown.
\r
199 if ((this._exports != null) && (this._imports != null))
\r
204 this._exports = GetExportDefinitions();
\r
205 this._imports = GetImportDefinitions();
\r
208 private IEnumerable<ExportDefinition> GetExportDefinitions()
\r
210 List<ExportDefinition> exports = new List<ExportDefinition>();
\r
212 this._contractNamesOnNonInterfaces = new HashSet<string>();
\r
214 // GetExportMembers should only contain the type itself along with the members declared on it,
\r
215 // it should not contain any base types, members on base types or interfaces on the type.
\r
216 foreach (MemberInfo member in GetExportMembers(this._type))
\r
218 foreach (ExportAttribute exportAttribute in member.GetAttributes<ExportAttribute>())
\r
220 var attributedExportDefinition = new AttributedExportDefinition(this, member, exportAttribute);
\r
222 if (exportAttribute.GetType() == CompositionServices.InheritedExportAttributeType)
\r
224 // Any InheritedExports on the type itself are contributed during this pass
\r
225 // and we need to do the book keeping for those.
\r
226 if (!this._contractNamesOnNonInterfaces.Contains(attributedExportDefinition.ContractName))
\r
228 exports.Add(new ReflectionMemberExportDefinition(member.ToLazyMember(), attributedExportDefinition, this));
\r
229 this._contractNamesOnNonInterfaces.Add(attributedExportDefinition.ContractName);
\r
234 exports.Add(new ReflectionMemberExportDefinition(member.ToLazyMember(), attributedExportDefinition, this));
\r
239 // GetInheritedExports should only contain InheritedExports on base types or interfaces.
\r
240 // The order of types returned here is important because it is used as a
\r
241 // priority list of which InhertedExport to choose if multiple exists with
\r
242 // the same contract name. Therefore ensure that we always return the types
\r
243 // in the hiearchy from most derived to the lowest base type, followed
\r
244 // by all the interfaces that this type implements.
\r
245 foreach (Type type in GetInheritedExports(this._type))
\r
247 foreach (InheritedExportAttribute exportAttribute in type.GetAttributes<InheritedExportAttribute>())
\r
249 var attributedExportDefinition = new AttributedExportDefinition(this, type, exportAttribute);
\r
251 if (!this._contractNamesOnNonInterfaces.Contains(attributedExportDefinition.ContractName))
\r
253 exports.Add(new ReflectionMemberExportDefinition(type.ToLazyMember(), attributedExportDefinition, this));
\r
255 if (!type.IsInterface)
\r
257 this._contractNamesOnNonInterfaces.Add(attributedExportDefinition.ContractName);
\r
263 this._contractNamesOnNonInterfaces = null; // No need to hold this state around any longer
\r
268 private IEnumerable<MemberInfo> GetExportMembers(Type type)
\r
270 BindingFlags flags = BindingFlags.DeclaredOnly | BindingFlags.Public |
\r
271 BindingFlags.NonPublic | BindingFlags.Instance | BindingFlags.Static;
\r
273 // If the type is abstract only find local static exports
\r
274 if (type.IsAbstract)
\r
276 flags &= ~BindingFlags.Instance;
\r
278 else if (IsExport(type))
\r
283 // Walk the fields
\r
284 foreach (var member in type.GetFields(flags))
\r
286 if (IsExport(member))
\r
288 yield return member;
\r
292 // Walk the properties
\r
293 foreach (var member in type.GetProperties(flags))
\r
295 if (IsExport(member))
\r
297 yield return member;
\r
301 // Walk the methods
\r
302 foreach (var member in type.GetMethods(flags))
\r
304 if (IsExport(member))
\r
306 yield return member;
\r
311 private IEnumerable<Type> GetInheritedExports(Type type)
\r
313 // If the type is abstract we aren't interested in type level exports
\r
314 if (type.IsAbstract)
\r
319 // The order of types returned here is important because it is used as a
\r
320 // priority list of which InhertedExport to choose if multiple exists with
\r
321 // the same contract name. Therefore ensure that we always return the types
\r
322 // in the hiearchy from most derived to the lowest base type, followed
\r
323 // by all the interfaces that this type implements.
\r
325 Type currentType = type.BaseType;
\r
327 if (currentType == null)
\r
332 // Stopping at object instead of null to help with performance. It is a noticable performance
\r
333 // gain (~5%) if we don't have to try and pull the attributes we know don't exist on object.
\r
334 // We also need the null check in case we're passed a type that doesn't live in the runtime context.
\r
335 while (currentType != null && currentType != CompositionServices.ObjectType)
\r
337 if (IsInheritedExport(currentType))
\r
339 yield return currentType;
\r
341 currentType = currentType.BaseType;
\r
344 foreach (Type iface in type.GetInterfaces())
\r
346 if (IsInheritedExport(iface))
\r
348 yield return iface;
\r
353 private static bool IsExport(ICustomAttributeProvider attributeProvider)
\r
355 return attributeProvider.IsAttributeDefined<ExportAttribute>(false);
\r
358 private static bool IsInheritedExport(ICustomAttributeProvider attributedProvider)
\r
360 return attributedProvider.IsAttributeDefined<InheritedExportAttribute>(false);
\r
363 private IEnumerable<ImportDefinition> GetImportDefinitions()
\r
365 List<ImportDefinition> imports = new List<ImportDefinition>();
\r
367 foreach (MemberInfo member in GetImportMembers(this._type))
\r
369 ReflectionMemberImportDefinition importDefinition = AttributedModelDiscovery.CreateMemberImportDefinition(member, this);
\r
370 imports.Add(importDefinition);
\r
373 var constructor = this.GetConstructor();
\r
375 if (constructor != null)
\r
377 foreach (ParameterInfo parameter in constructor.GetParameters())
\r
379 ReflectionParameterImportDefinition importDefinition = AttributedModelDiscovery.CreateParameterImportDefinition(parameter, this);
\r
380 imports.Add(importDefinition);
\r
387 private IEnumerable<MemberInfo> GetImportMembers(Type type)
\r
389 if (type.IsAbstract)
\r
394 foreach (MemberInfo member in GetDeclaredOnlyImportMembers(type))
\r
396 yield return member;
\r
399 // Walk up the type chain until you hit object.
\r
400 if (type.BaseType != null)
\r
402 Type baseType = type.BaseType;
\r
404 // Stopping at object instead of null to help with performance. It is a noticable performance
\r
405 // gain (~5%) if we don't have to try and pull the attributes we know don't exist on object.
\r
406 // We also need the null check in case we're passed a type that doesn't live in the runtime context.
\r
407 while (baseType != null && baseType != CompositionServices.ObjectType)
\r
409 foreach (MemberInfo member in GetDeclaredOnlyImportMembers(baseType))
\r
411 yield return member;
\r
413 baseType = baseType.BaseType;
\r
418 private IEnumerable<MemberInfo> GetDeclaredOnlyImportMembers(Type type)
\r
420 BindingFlags flags = BindingFlags.DeclaredOnly | BindingFlags.Public | BindingFlags.NonPublic | BindingFlags.Instance;
\r
422 // Walk the fields
\r
423 foreach (var member in type.GetFields(flags))
\r
425 if (IsImport(member))
\r
427 yield return member;
\r
431 // Walk the properties
\r
432 foreach (var member in type.GetProperties(flags))
\r
434 if (IsImport(member))
\r
436 yield return member;
\r
441 private static bool IsImport(ICustomAttributeProvider attributeProvider)
\r
443 return attributeProvider.IsAttributeDefined<IAttributedImport>(false);
\r