| File: System\ComponentModel\Composition\Hosting\TypeCatalog.cs | Web Access |
| Project: src\runtime\src\libraries\System.ComponentModel.Composition\src\System.ComponentModel.Composition.csproj (System.ComponentModel.Composition) |
// Licensed to the .NET Foundation under one or more agreements. // The .NET Foundation licenses this file to you under the MIT license. using System.Collections.Generic; using System.ComponentModel.Composition.AttributedModel; using System.ComponentModel.Composition.Primitives; using System.ComponentModel.Composition.ReflectionModel; using System.Diagnostics; using System.Diagnostics.CodeAnalysis; using System.Globalization; using System.Linq; using System.Reflection; using System.Text; using System.Threading; using Microsoft.Internal; using Microsoft.Internal.Collections; namespace System.ComponentModel.Composition.Hosting { /// <summary> /// An immutable ComposablePartCatalog created from a type array or a list of managed types. This class is threadsafe. /// It is Disposable. /// </summary> [DebuggerTypeProxy(typeof(ComposablePartCatalogDebuggerProxy))] public class TypeCatalog : ComposablePartCatalog, ICompositionElement { private readonly object _thisLock = new object(); private Type[]? _types; private List<ComposablePartDefinition>? _parts; private volatile bool _isDisposed; private readonly ICompositionElement _definitionOrigin; private readonly Lazy<Dictionary<string, List<ComposablePartDefinition>>> _contractPartIndex; /// <summary> /// Initializes a new instance of the <see cref="TypeCatalog"/> class /// with the specified types. /// </summary> /// <param name="types"> /// An <see cref="Array"/> of attributed <see cref="Type"/> objects to add to the /// <see cref="TypeCatalog"/>. /// </param> /// <exception cref="ArgumentNullException"> /// <paramref name="types"/> is <see langword="null"/>. /// </exception> /// <exception cref="ArgumentException"> /// <paramref name="types"/> contains an element that is <see langword="null"/>. /// <para> /// -or- /// </para> /// <paramref name="types"/> contains an element that was loaded in the Reflection-only context. /// </exception> public TypeCatalog(params Type[] types) : this((IEnumerable<Type>)types) { } /// <summary> /// Initializes a new instance of the <see cref="TypeCatalog"/> class /// with the specified types. /// </summary> /// <param name="types"> /// An <see cref="IEnumerable{T}"/> of attributed <see cref="Type"/> objects to add /// to the <see cref="TypeCatalog"/>. /// </param> /// <exception cref="ArgumentNullException"> /// <paramref name="types"/> is <see langword="null"/>. /// </exception> /// <exception cref="ArgumentException"> /// <paramref name="types"/> contains an element that is <see langword="null"/>. /// <para> /// -or- /// </para> /// <paramref name="types"/> contains an element that was loaded in the reflection-only context. /// </exception> public TypeCatalog(IEnumerable<Type> types) { Requires.NotNull(types, nameof(types)); InitializeTypeCatalog(types); _definitionOrigin = this; _contractPartIndex = new Lazy<Dictionary<string, List<ComposablePartDefinition>>>(CreateIndex, true); } /// <summary> /// Initializes a new instance of the <see cref="TypeCatalog"/> class /// with the specified types. /// </summary> /// <param name="types"> /// An <see cref="IEnumerable{T}"/> of attributed <see cref="Type"/> objects to add /// to the <see cref="TypeCatalog"/>. /// </param> /// <exception cref="ArgumentNullException"> /// <paramref name="types"/> is <see langword="null"/>. /// </exception> /// <param name="definitionOrigin"> /// The <see cref="ICompositionElement"/> CompositionElement used by Diagnostics to identify the source for parts. /// </param> /// <exception cref="ArgumentException"> /// <paramref name="types"/> contains an element that is <see langword="null"/>. /// </exception> public TypeCatalog(IEnumerable<Type> types, ICompositionElement definitionOrigin) { Requires.NotNull(types, nameof(types)); Requires.NotNull(definitionOrigin, nameof(definitionOrigin)); InitializeTypeCatalog(types); _definitionOrigin = definitionOrigin; _contractPartIndex = new Lazy<Dictionary<string, List<ComposablePartDefinition>>>(CreateIndex, true); } /// <summary> /// Initializes a new instance of the <see cref="TypeCatalog"/> class /// with the specified types. /// </summary> /// <param name="types"> /// An <see cref="IEnumerable{T}"/> of attributed <see cref="Type"/> objects to add /// to the <see cref="TypeCatalog"/>. /// </param> /// <exception cref="ArgumentNullException"> /// <paramref name="types"/> is <see langword="null"/>. /// </exception> /// <param name="reflectionContext"> /// The <see cref="ReflectionContext"/> a context used by the catalog when /// interpreting the types to inject attributes into the type definition. /// </param> /// <exception cref="ArgumentException"> /// <paramref name="types"/> contains an element that is <see langword="null"/>. /// </exception> public TypeCatalog(IEnumerable<Type> types, ReflectionContext reflectionContext) { Requires.NotNull(types, nameof(types)); Requires.NotNull(reflectionContext, nameof(reflectionContext)); InitializeTypeCatalog(types, reflectionContext); _definitionOrigin = this; _contractPartIndex = new Lazy<Dictionary<string, List<ComposablePartDefinition>>>(CreateIndex, true); } /// <summary> /// Initializes a new instance of the <see cref="TypeCatalog"/> class /// with the specified types. /// </summary> /// <param name="types"> /// An <see cref="IEnumerable{T}"/> of attributed <see cref="Type"/> objects to add /// to the <see cref="TypeCatalog"/>. /// </param> /// <param name="reflectionContext"> /// The <see cref="ReflectionContext"/> a context used by the catalog when /// interpreting the types to inject attributes into the type definition. /// </param> /// <param name="definitionOrigin"> /// The <see cref="ICompositionElement"/> CompositionElement used by Diagnostics to identify the source for parts. /// </param> /// <exception cref="ArgumentNullException"> /// <paramref name="types"/> is <see langword="null"/>. /// </exception> /// <exception cref="ArgumentException"> /// <paramref name="types"/> contains an element that is <see langword="null"/>. /// </exception> public TypeCatalog(IEnumerable<Type> types, ReflectionContext reflectionContext, ICompositionElement definitionOrigin) { Requires.NotNull(types, nameof(types)); Requires.NotNull(reflectionContext, nameof(reflectionContext)); Requires.NotNull(definitionOrigin, nameof(definitionOrigin)); InitializeTypeCatalog(types, reflectionContext); _definitionOrigin = definitionOrigin; _contractPartIndex = new Lazy<Dictionary<string, List<ComposablePartDefinition>>>(CreateIndex, true); } private void InitializeTypeCatalog(IEnumerable<Type> types, ReflectionContext reflectionContext) { var typesList = new List<Type>(); foreach (var type in types) { if (type == null) { throw ExceptionBuilder.CreateContainsNullElement(nameof(types)); } if (type.Assembly.ReflectionOnly) { throw new ArgumentException(SR.Format(SR.Argument_ElementReflectionOnlyType, nameof(types)), nameof(types)); } var typeInfo = type.GetTypeInfo(); var lclType = (reflectionContext != null) ? reflectionContext.MapType(typeInfo) : typeInfo; // It is valid for the reflectionContext to delete types by mapping them to null if (lclType != null) { // The final mapped type may be activated so we check to see if it is in a reflect only assembly if (lclType.Assembly.ReflectionOnly) { throw new ArgumentException(SR.Format(SR.Argument_ReflectionContextReturnsReflectionOnlyType, nameof(reflectionContext)), nameof(reflectionContext)); } typesList.Add(lclType); } } _types = typesList.ToArray(); } private void InitializeTypeCatalog(IEnumerable<Type> types) { Type[] arr = types.ToArray(); foreach (Type type in arr) { if (type == null) { throw ExceptionBuilder.CreateContainsNullElement(nameof(types)); } if (type.Assembly.ReflectionOnly) { throw new ArgumentException(SR.Format(SR.Argument_ElementReflectionOnlyType, nameof(types)), nameof(types)); } } _types = arr; } public override IEnumerator<ComposablePartDefinition> GetEnumerator() { ThrowIfDisposed(); return PartsInternal.GetEnumerator(); } /// <summary> /// Gets the display name of the type catalog. /// </summary> /// <value> /// A <see cref="string"/> containing a human-readable display name of the <see cref="TypeCatalog"/>. /// </value> string ICompositionElement.DisplayName { get { return GetDisplayName(); } } /// <summary> /// Gets the composition element from which the type catalog originated. /// </summary> /// <value> /// This property always returns <see langword="null"/>. /// </value> ICompositionElement? ICompositionElement.Origin { get { return null; } } private IEnumerable<ComposablePartDefinition> PartsInternal { get { if (_parts == null) { lock (_thisLock) { if (_parts == null) { if (_types == null) { throw new Exception(SR.Diagnostic_InternalExceptionMessage); } var collection = new List<ComposablePartDefinition>(); foreach (Type type in _types) { var definition = AttributedModelDiscovery.CreatePartDefinitionIfDiscoverable(type, _definitionOrigin); if (definition != null) { collection.Add(definition); } } Thread.MemoryBarrier(); _types = null; _parts = collection; } } } return _parts; } } internal override IEnumerable<ComposablePartDefinition>? GetCandidateParts(ImportDefinition definition) { ArgumentNullException.ThrowIfNull(definition); string contractName = definition.ContractName; if (string.IsNullOrEmpty(contractName)) { return PartsInternal; } string? genericContractName = definition.Metadata.GetValue<string>(CompositionConstants.GenericContractMetadataName); List<ComposablePartDefinition>? nonGenericMatches = GetCandidateParts(contractName); List<ComposablePartDefinition>? genericMatches = GetCandidateParts(genericContractName); return nonGenericMatches.ConcatAllowingNull(genericMatches); } private List<ComposablePartDefinition>? GetCandidateParts(string? contractName) { if (contractName == null) { return null; } _contractPartIndex.Value.TryGetValue(contractName, out List<ComposablePartDefinition>? contractCandidateParts); return contractCandidateParts; } private Dictionary<string, List<ComposablePartDefinition>> CreateIndex() { Dictionary<string, List<ComposablePartDefinition>> index = new Dictionary<string, List<ComposablePartDefinition>>(StringComparers.ContractName); foreach (var part in PartsInternal) { foreach (string contractName in part.ExportDefinitions.Select(export => export.ContractName).Distinct()) { List<ComposablePartDefinition>? contractParts = null; if (!index.TryGetValue(contractName, out contractParts)) { contractParts = new List<ComposablePartDefinition>(); index.Add(contractName, contractParts); } contractParts.Add(part); } } return index; } /// <summary> /// Returns a string representation of the type catalog. /// </summary> /// <returns> /// A <see cref="string"/> containing the string representation of the <see cref="TypeCatalog"/>. /// </returns> public override string ToString() { return GetDisplayName(); } protected override void Dispose(bool disposing) { if (disposing) { _isDisposed = true; } base.Dispose(disposing); } private string GetDisplayName() { return SR.Format( SR.TypeCatalog_DisplayNameFormat, GetType().Name, GetTypesDisplay()); } private string GetTypesDisplay() { int count = PartsInternal.Count(); if (count == 0) { return SR.TypeCatalog_Empty; } const int displayCount = 2; StringBuilder builder = new StringBuilder(); foreach (ReflectionComposablePartDefinition definition in PartsInternal.Take(displayCount)) { if (builder.Length > 0) { builder.Append(CultureInfo.CurrentCulture.TextInfo.ListSeparator); builder.Append(' '); } builder.Append(definition.GetPartType().GetDisplayName()); } if (count > displayCount) { // Add an elipse to indicate that there // are more types than actually listed builder.Append(CultureInfo.CurrentCulture.TextInfo.ListSeparator); builder.Append(" ..."); } return builder.ToString(); } [DebuggerStepThrough] private void ThrowIfDisposed() { if (_isDisposed) { throw ExceptionBuilder.CreateObjectDisposed(this); } } } }