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 if (this._exports != null)
\r
201 this._exports = GetExportDefinitions();
\r
202 this._imports = GetImportDefinitions();
\r
205 private IEnumerable<ExportDefinition> GetExportDefinitions()
\r
207 List<ExportDefinition> exports = new List<ExportDefinition>();
\r
209 this._contractNamesOnNonInterfaces = new HashSet<string>();
\r
211 // GetExportMembers should only contain the type itself along with the members declared on it,
\r
212 // it should not contain any base types, members on base types or interfaces on the type.
\r
213 foreach (MemberInfo member in GetExportMembers(this._type))
\r
215 foreach (ExportAttribute exportAttribute in member.GetAttributes<ExportAttribute>())
\r
217 var attributedExportDefinition = new AttributedExportDefinition(this, member, exportAttribute);
\r
219 if (exportAttribute.GetType() == CompositionServices.InheritedExportAttributeType)
\r
221 // Any InheritedExports on the type itself are contributed during this pass
\r
222 // and we need to do the book keeping for those.
\r
223 if (!this._contractNamesOnNonInterfaces.Contains(attributedExportDefinition.ContractName))
\r
225 exports.Add(new ReflectionMemberExportDefinition(member.ToLazyMember(), attributedExportDefinition, this));
\r
226 this._contractNamesOnNonInterfaces.Add(attributedExportDefinition.ContractName);
\r
231 exports.Add(new ReflectionMemberExportDefinition(member.ToLazyMember(), attributedExportDefinition, this));
\r
236 // GetInheritedExports should only contain InheritedExports on base types or interfaces.
\r
237 // The order of types returned here is important because it is used as a
\r
238 // priority list of which InhertedExport to choose if multiple exists with
\r
239 // the same contract name. Therefore ensure that we always return the types
\r
240 // in the hiearchy from most derived to the lowest base type, followed
\r
241 // by all the interfaces that this type implements.
\r
242 foreach (Type type in GetInheritedExports(this._type))
\r
244 foreach (InheritedExportAttribute exportAttribute in type.GetAttributes<InheritedExportAttribute>())
\r
246 var attributedExportDefinition = new AttributedExportDefinition(this, type, exportAttribute);
\r
248 if (!this._contractNamesOnNonInterfaces.Contains(attributedExportDefinition.ContractName))
\r
250 exports.Add(new ReflectionMemberExportDefinition(type.ToLazyMember(), attributedExportDefinition, this));
\r
252 if (!type.IsInterface)
\r
254 this._contractNamesOnNonInterfaces.Add(attributedExportDefinition.ContractName);
\r
260 this._contractNamesOnNonInterfaces = null; // No need to hold this state around any longer
\r
265 private IEnumerable<MemberInfo> GetExportMembers(Type type)
\r
267 BindingFlags flags = BindingFlags.DeclaredOnly | BindingFlags.Public |
\r
268 BindingFlags.NonPublic | BindingFlags.Instance | BindingFlags.Static;
\r
270 // If the type is abstract only find local static exports
\r
271 if (type.IsAbstract)
\r
273 flags &= ~BindingFlags.Instance;
\r
275 else if (IsExport(type))
\r
280 // Walk the fields
\r
281 foreach (var member in type.GetFields(flags))
\r
283 if (IsExport(member))
\r
285 yield return member;
\r
289 // Walk the properties
\r
290 foreach (var member in type.GetProperties(flags))
\r
292 if (IsExport(member))
\r
294 yield return member;
\r
298 // Walk the methods
\r
299 foreach (var member in type.GetMethods(flags))
\r
301 if (IsExport(member))
\r
303 yield return member;
\r
308 private IEnumerable<Type> GetInheritedExports(Type type)
\r
310 // If the type is abstract we aren't interested in type level exports
\r
311 if (type.IsAbstract)
\r
316 // The order of types returned here is important because it is used as a
\r
317 // priority list of which InhertedExport to choose if multiple exists with
\r
318 // the same contract name. Therefore ensure that we always return the types
\r
319 // in the hiearchy from most derived to the lowest base type, followed
\r
320 // by all the interfaces that this type implements.
\r
322 Type currentType = type.BaseType;
\r
324 if (currentType == null)
\r
329 // Stopping at object instead of null to help with performance. It is a noticable performance
\r
330 // gain (~5%) if we don't have to try and pull the attributes we know don't exist on object.
\r
331 while (currentType != CompositionServices.ObjectType)
\r
333 if (IsInheritedExport(currentType))
\r
335 yield return currentType;
\r
337 currentType = currentType.BaseType;
\r
340 foreach (Type iface in type.GetInterfaces())
\r
342 if (IsInheritedExport(iface))
\r
344 yield return iface;
\r
349 private static bool IsExport(ICustomAttributeProvider attributeProvider)
\r
351 return attributeProvider.IsAttributeDefined<ExportAttribute>(false);
\r
354 private static bool IsInheritedExport(ICustomAttributeProvider attributedProvider)
\r
356 return attributedProvider.IsAttributeDefined<InheritedExportAttribute>(false);
\r
359 private IEnumerable<ImportDefinition> GetImportDefinitions()
\r
361 List<ImportDefinition> imports = new List<ImportDefinition>();
\r
363 foreach (MemberInfo member in GetImportMembers(this._type))
\r
365 ReflectionMemberImportDefinition importDefinition = AttributedModelDiscovery.CreateMemberImportDefinition(member, this);
\r
366 imports.Add(importDefinition);
\r
369 var constructor = this.GetConstructor();
\r
371 if (constructor != null)
\r
373 foreach (ParameterInfo parameter in constructor.GetParameters())
\r
375 ReflectionParameterImportDefinition importDefinition = AttributedModelDiscovery.CreateParameterImportDefinition(parameter, this);
\r
376 imports.Add(importDefinition);
\r
383 private IEnumerable<MemberInfo> GetImportMembers(Type type)
\r
385 if (type.IsAbstract)
\r
390 foreach (MemberInfo member in GetDeclaredOnlyImportMembers(type))
\r
392 yield return member;
\r
395 // Walk up the type chain until you hit object.
\r
396 if (type.BaseType != null)
\r
398 Type baseType = type.BaseType;
\r
400 // Stopping at object instead of null to help with performance. It is a noticable performance
\r
401 // gain (~5%) if we don't have to try and pull the attributes we know don't exist on object.
\r
402 while (baseType != CompositionServices.ObjectType)
\r
404 foreach (MemberInfo member in GetDeclaredOnlyImportMembers(baseType))
\r
406 yield return member;
\r
408 baseType = baseType.BaseType;
\r
413 private IEnumerable<MemberInfo> GetDeclaredOnlyImportMembers(Type type)
\r
415 BindingFlags flags = BindingFlags.DeclaredOnly | BindingFlags.Public | BindingFlags.NonPublic | BindingFlags.Instance;
\r
417 // Walk the fields
\r
418 foreach (var member in type.GetFields(flags))
\r
420 if (IsImport(member))
\r
422 yield return member;
\r
426 // Walk the properties
\r
427 foreach (var member in type.GetProperties(flags))
\r
429 if (IsImport(member))
\r
431 yield return member;
\r
436 private static bool IsImport(ICustomAttributeProvider attributeProvider)
\r
438 return attributeProvider.IsAttributeDefined<IAttributedImport>(false);
\r