| File: System\ComponentModel\Composition\ReflectionModel\ReflectionComposablePartDefinition.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.Hosting; using System.ComponentModel.Composition.Primitives; using System.Diagnostics.CodeAnalysis; using System.Linq; using System.Reflection; using Microsoft.Internal.Collections; namespace System.ComponentModel.Composition.ReflectionModel { internal sealed class ReflectionComposablePartDefinition : ComposablePartDefinition, ICompositionElement { private readonly IReflectionPartCreationInfo _creationInfo; private ImportDefinition[]? _imports; private ExportDefinition[]? _exports; private IDictionary<string, object?>? _metadata; private ConstructorInfo? _constructor; private readonly object _lock = new object(); public ReflectionComposablePartDefinition(IReflectionPartCreationInfo creationInfo) { ArgumentNullException.ThrowIfNull(creationInfo); _creationInfo = creationInfo; } public Type GetPartType() { return _creationInfo.GetPartType(); } public Lazy<Type> GetLazyPartType() { return _creationInfo.GetLazyPartType(); } public ConstructorInfo? GetConstructor() { if (_constructor == null) { ConstructorInfo? constructor = _creationInfo.GetConstructor(); lock (_lock) { _constructor ??= constructor; } } return _constructor; } private ExportDefinition[] ExportDefinitionsInternal { get { if (_exports == null) { ExportDefinition[] exports = _creationInfo.GetExports().ToArray(); lock (_lock) { _exports ??= exports; } } return _exports; } } public override IEnumerable<ExportDefinition> ExportDefinitions { get { return ExportDefinitionsInternal; } } public override IEnumerable<ImportDefinition> ImportDefinitions { get { if (_imports == null) { ImportDefinition[] imports = _creationInfo.GetImports().ToArray(); lock (_lock) { _imports ??= imports; } } return _imports; } } public override IDictionary<string, object?> Metadata { get { if (_metadata == null) { IDictionary<string, object?> metadata = _creationInfo.GetMetadata().AsReadOnly(); lock (_lock) { _metadata ??= metadata; } } return _metadata; } } internal bool IsDisposalRequired { get { return _creationInfo.IsDisposalRequired; } } public override ComposablePart CreatePart() { if (IsDisposalRequired) { return new DisposableReflectionComposablePart(this); } else { return new ReflectionComposablePart(this); } } internal override ComposablePartDefinition? GetGenericPartDefinition() { if (_creationInfo is GenericSpecializationPartCreationInfo genericCreationInfo) { return genericCreationInfo.OriginalPart; } return null; } internal override bool TryGetExports(ImportDefinition definition, out Tuple<ComposablePartDefinition, ExportDefinition>? singleMatch, out IEnumerable<Tuple<ComposablePartDefinition, ExportDefinition>>? multipleMatches) { if (this.IsGeneric()) { singleMatch = null; multipleMatches = null; List<Tuple<ComposablePartDefinition, ExportDefinition>>? exports = null; var genericParameters = (definition.Metadata.Count > 0) ? definition.Metadata.GetValue<IEnumerable<object>>(CompositionConstants.GenericParametersMetadataName) : null; // if and only if generic parameters have been supplied can we attempt to "close" the generic if (genericParameters != null) { // we only understand types if (TryGetGenericTypeParameters(genericParameters, out Type?[]? genericTypeParameters)) { HashSet<ComposablePartDefinition>? candidates = null; ComposablePartDefinition? previousPart = null; // go through all orders of generic parameters that part exports allows foreach (Type[] candidateParameters in GetCandidateParameters(genericTypeParameters!)) { if (TryMakeGenericPartDefinition(candidateParameters, out ComposablePartDefinition? candidatePart)) { bool alreadyProcessed = false; if (candidates == null) { if (previousPart != null) { if (candidatePart.Equals(previousPart)) { alreadyProcessed = true; } else { candidates = new HashSet<ComposablePartDefinition>(); candidates.Add(previousPart); candidates.Add(candidatePart); } } else { previousPart = candidatePart; } } else { alreadyProcessed |= !candidates.Add(candidatePart); } if (!alreadyProcessed) { if (candidatePart.TryGetExports(definition, out Tuple<ComposablePartDefinition, ExportDefinition>? candidateSingleMatch, out IEnumerable<Tuple<ComposablePartDefinition, ExportDefinition>>? candidateMultipleMatches)) { exports = exports.FastAppendToListAllowNulls(candidateSingleMatch, candidateMultipleMatches); } } } } } } if (exports != null) { multipleMatches = exports; return true; } else { return false; } } else { return TryGetNonGenericExports(definition, out singleMatch, out multipleMatches); } } // Optimised for local as array case private bool TryGetNonGenericExports(ImportDefinition definition, out Tuple<ComposablePartDefinition, ExportDefinition>? singleMatch, out IEnumerable<Tuple<ComposablePartDefinition, ExportDefinition>>? multipleMatches) { singleMatch = null; multipleMatches = null; List<Tuple<ComposablePartDefinition, ExportDefinition>>? multipleExports = null; Tuple<ComposablePartDefinition, ExportDefinition>? singleExport = null; bool matchesFound = false; foreach (var export in ExportDefinitionsInternal) { if (definition.IsConstraintSatisfiedBy(export)) { matchesFound = true; if (singleExport == null) { singleExport = new Tuple<ComposablePartDefinition, ExportDefinition>(this, export); } else { if (multipleExports == null) { multipleExports = new List<Tuple<ComposablePartDefinition, ExportDefinition>>(); multipleExports.Add(singleExport); } multipleExports.Add(new Tuple<ComposablePartDefinition, ExportDefinition>(this, export)); } } } if (!matchesFound) { return false; } if (multipleExports != null) { multipleMatches = multipleExports; } else { singleMatch = singleExport; } return true; } private IEnumerable<Type[]> GetCandidateParameters(Type[] genericParameters) { // we iterate over all exports and find only generic ones. Assuming the arity matches, we reorder the original parameters foreach (ExportDefinition export in ExportDefinitionsInternal) { var genericParametersOrder = export.Metadata.GetValue<int[]>(CompositionConstants.GenericExportParametersOrderMetadataName); if ((genericParametersOrder != null) && (genericParametersOrder.Length == genericParameters.Length)) { yield return GenericServices.Reorder(genericParameters, genericParametersOrder); } } } private static bool TryGetGenericTypeParameters(IEnumerable<object> genericParameters, [NotNullWhen(true)] out Type?[]? genericTypeParameters) { genericTypeParameters = genericParameters as Type[]; if (genericTypeParameters == null) { object[] genericParametersAsArray = genericParameters.AsArray(); genericTypeParameters = new Type[genericParametersAsArray.Length]; for (int i = 0; i < genericParametersAsArray.Length; i++) { genericTypeParameters[i] = genericParametersAsArray[i] as Type; if (genericTypeParameters[i] == null) { return false; } } } return true; } internal bool TryMakeGenericPartDefinition(Type[] genericTypeParameters, [NotNullWhen(true)] out ComposablePartDefinition? genericPartDefinition) { genericPartDefinition = null; if (!GenericSpecializationPartCreationInfo.CanSpecialize(Metadata, genericTypeParameters)) { return false; } genericPartDefinition = new ReflectionComposablePartDefinition(new GenericSpecializationPartCreationInfo(_creationInfo, this, genericTypeParameters)); return true; } string ICompositionElement.DisplayName { get { return _creationInfo.DisplayName; } } ICompositionElement? ICompositionElement.Origin { get { return _creationInfo.Origin; } } public override string ToString() { return _creationInfo.DisplayName; } public override bool Equals(object? obj) { if (_creationInfo.IsIdentityComparison) { return object.ReferenceEquals(this, obj); } else { return obj is ReflectionComposablePartDefinition that && _creationInfo.Equals(that._creationInfo); } } public override int GetHashCode() { if (_creationInfo.IsIdentityComparison) { return base.GetHashCode(); } else { return _creationInfo.GetHashCode(); } } } }