Refactoring
This commit is contained in:
Родитель
7c0a0afa69
Коммит
55e5eff24f
|
@ -80,7 +80,7 @@ namespace Unity.Builder
|
|||
if (LifetimeManager.NoValue != value) return value;
|
||||
}
|
||||
|
||||
return Resolve(pipeline);
|
||||
return Resolve(type, name, pipeline);
|
||||
}
|
||||
|
||||
#endregion
|
||||
|
@ -381,7 +381,7 @@ namespace Unity.Builder
|
|||
if (LifetimeManager.NoValue != value) return value;
|
||||
}
|
||||
|
||||
return Resolve(pipeline);
|
||||
return Resolve(type, null, pipeline);
|
||||
}
|
||||
|
||||
public object? Resolve(Type type, IRegistration registration)
|
||||
|
@ -441,26 +441,36 @@ namespace Unity.Builder
|
|||
}
|
||||
}
|
||||
|
||||
public object? Resolve(ResolveDelegate<BuilderContext> pipeline)
|
||||
public object? Resolve(Type type, string? name, ResolveDelegate<BuilderContext> pipeline)
|
||||
{
|
||||
// Setup Context
|
||||
var synchronized = pipeline.Target as SynchronizedLifetimeManager;
|
||||
var thisContext = this;
|
||||
|
||||
try
|
||||
unsafe
|
||||
{
|
||||
// Execute pipeline
|
||||
var value = pipeline(ref thisContext);
|
||||
(pipeline.Target as LifetimeManager)?.Set(value, ContainerContext.Lifetime);
|
||||
// Setup Context
|
||||
var context = new BuilderContext
|
||||
{
|
||||
ContainerContext = pipeline.Target is ContainerControlledLifetimeManager containerControlled
|
||||
? (ContainerContext)containerControlled.Scope
|
||||
: ContainerContext,
|
||||
IsAsync = IsAsync,
|
||||
List = List,
|
||||
Type = type,
|
||||
Name = name,
|
||||
Overrides = Overrides,
|
||||
DeclaringType = Type,
|
||||
#if !NET40
|
||||
Parent = new IntPtr(Unsafe.AsPointer(ref thisContext))
|
||||
#endif
|
||||
};
|
||||
|
||||
var manager = pipeline.Target as LifetimeManager;
|
||||
var value = pipeline(ref context);
|
||||
manager?.SetValue(value, ContainerContext.Lifetime);
|
||||
return value;
|
||||
}
|
||||
catch when (null != synchronized)
|
||||
{
|
||||
synchronized.Recover();
|
||||
throw;
|
||||
}
|
||||
}
|
||||
|
||||
#endregion
|
||||
}
|
||||
}
|
||||
}
|
||||
|
|
|
@ -1,4 +1,5 @@
|
|||
using System;
|
||||
using System.Diagnostics;
|
||||
using System.Linq;
|
||||
using System.Reflection;
|
||||
using Unity.Builder;
|
||||
|
@ -12,7 +13,7 @@ namespace Unity.Factories
|
|||
|
||||
private static readonly MethodInfo EnumerableMethod =
|
||||
typeof(EnumerableResolver).GetTypeInfo()
|
||||
.GetDeclaredMethod(nameof(EnumerableResolver.Resolver));
|
||||
.GetDeclaredMethod(nameof(EnumerableResolver.EnumerableHandler));
|
||||
|
||||
private static readonly MethodInfo EnumerableFactory =
|
||||
typeof(EnumerableResolver).GetTypeInfo()
|
||||
|
@ -29,6 +30,7 @@ namespace Unity.Factories
|
|||
var typeArgument = type.GetTypeInfo().GenericTypeArguments.First();
|
||||
if (typeArgument.GetTypeInfo().IsGenericType)
|
||||
#else
|
||||
Debug.Assert(0 < type.GenericTypeArguments.Length);
|
||||
var typeArgument = type.GenericTypeArguments.First();
|
||||
if (typeArgument.IsGenericType)
|
||||
#endif
|
||||
|
@ -50,7 +52,7 @@ namespace Unity.Factories
|
|||
|
||||
#region Implementation
|
||||
|
||||
private static object Resolver<TElement>(ref BuilderContext context)
|
||||
private static object EnumerableHandler<TElement>(ref BuilderContext context)
|
||||
{
|
||||
return ((UnityContainer)context.Container).ResolveEnumerable<TElement>(context.Resolve,
|
||||
context.Name);
|
||||
|
|
|
@ -46,7 +46,7 @@ namespace Unity.Factories
|
|||
var lifetime = context.Get(typeof(LifetimeManager));
|
||||
if (lifetime is PerResolveLifetimeManager)
|
||||
{
|
||||
var perBuildLifetime = new InternalPerResolveLifetimeManager(context.Existing);
|
||||
var perBuildLifetime = new RuntimePerResolveLifetimeManager(context.Existing);
|
||||
context.Set(typeof(LifetimeManager), perBuildLifetime);
|
||||
}
|
||||
|
||||
|
|
|
@ -0,0 +1,23 @@
|
|||
using System;
|
||||
using System.Diagnostics;
|
||||
|
||||
namespace Unity.Lifetime
|
||||
{
|
||||
internal class ContainerLifetimeManager : LifetimeManager
|
||||
{
|
||||
protected override LifetimeManager OnCreateLifetimeManager()
|
||||
=> throw new NotImplementedException();
|
||||
|
||||
public override object GetValue(ILifetimeContainer container)
|
||||
{
|
||||
Debug.Assert(null != container);
|
||||
return container.Container;
|
||||
}
|
||||
|
||||
public override object TryGetValue(ILifetimeContainer container)
|
||||
{
|
||||
Debug.Assert(null != container);
|
||||
return container.Container;
|
||||
}
|
||||
}
|
||||
}
|
|
@ -7,7 +7,7 @@ namespace Unity.Lifetime
|
|||
/// but also provides a signal to the default build plan, marking the type so that
|
||||
/// instances are reused across the build up object graph.
|
||||
/// </summary>
|
||||
internal class InternalPerResolveLifetimeManager : PerResolveLifetimeManager
|
||||
internal class RuntimePerResolveLifetimeManager : PerResolveLifetimeManager
|
||||
{
|
||||
/// <summary>
|
||||
/// Construct a new <see cref="PerResolveLifetimeManager"/> object that stores the
|
||||
|
@ -17,7 +17,7 @@ namespace Unity.Lifetime
|
|||
/// normal <see cref="LifetimeManager.SetValue"/> method is not used here.
|
||||
/// </summary>
|
||||
/// <param name="value">InjectionParameterValue to store.</param>
|
||||
public InternalPerResolveLifetimeManager(object? value)
|
||||
public RuntimePerResolveLifetimeManager(object? value)
|
||||
{
|
||||
base.value = value;
|
||||
InUse = true;
|
|
@ -417,7 +417,7 @@ namespace Unity
|
|||
}
|
||||
|
||||
context.Set(typeof(LifetimeManager),
|
||||
new InternalPerResolveLifetimeManager(context.Existing));
|
||||
new RuntimePerResolveLifetimeManager(context.Existing));
|
||||
}
|
||||
|
||||
return null == pipeline ? context.Existing : pipeline.Invoke(ref context);
|
||||
|
|
|
@ -11,7 +11,7 @@ namespace Unity
|
|||
{
|
||||
#region Fields
|
||||
|
||||
private static readonly ConstructorInfo PerResolveInfo = typeof(InternalPerResolveLifetimeManager)
|
||||
private static readonly ConstructorInfo PerResolveInfo = typeof(RuntimePerResolveLifetimeManager)
|
||||
.GetTypeInfo().DeclaredConstructors.First();
|
||||
|
||||
protected static readonly Expression SetPerBuildSingletonExpr =
|
||||
|
|
|
@ -122,7 +122,7 @@ namespace Unity
|
|||
|
||||
context.Existing = info.Invoke(dependencies);
|
||||
context.Set(typeof(LifetimeManager),
|
||||
new InternalPerResolveLifetimeManager(context.Existing));
|
||||
new RuntimePerResolveLifetimeManager(context.Existing));
|
||||
}
|
||||
|
||||
return null == pipeline ? context.Existing : pipeline.Invoke(ref context);
|
||||
|
|
|
@ -43,8 +43,6 @@ namespace Unity
|
|||
// Try finding factory
|
||||
TypeFactoryDelegate? factory = builder.Policies?.Get<TypeFactoryDelegate>();
|
||||
|
||||
if (builder.Policies is ExplicitRegistration @explicit)
|
||||
{
|
||||
#if NETCOREAPP1_0 || NETSTANDARD1_0
|
||||
if (null != builder.Type && builder.Type.GetTypeInfo().IsGenericType)
|
||||
#else
|
||||
|
@ -67,29 +65,6 @@ namespace Unity
|
|||
return (ref BuilderContext context) => throw new InvalidRegistrationException(message);
|
||||
}
|
||||
}
|
||||
}
|
||||
else if(builder.Policies is ImplicitRegistration @implicit)
|
||||
{
|
||||
#if NETCOREAPP1_0 || NETSTANDARD1_0
|
||||
if (null != builder.Type && builder.Type.GetTypeInfo().IsGenericType)
|
||||
#else
|
||||
if (null != builder.Type && builder.Type.IsGenericType)
|
||||
#endif
|
||||
{
|
||||
factory = (TypeFactoryDelegate?)builder.ContainerContext.Get(builder.Type.GetGenericTypeDefinition(),
|
||||
typeof(TypeFactoryDelegate));
|
||||
}
|
||||
else if (builder.Type?.IsArray ?? false)
|
||||
{
|
||||
if (builder.Type?.GetArrayRank() == 1)
|
||||
return builder.Pipeline(ArrayResolver.Factory(builder.Type, builder.ContainerContext.Container));
|
||||
else
|
||||
{
|
||||
var message = $"Invalid array {builder.Type}. Only arrays of rank 1 are supported";
|
||||
return (ref BuilderContext context) => throw new InvalidRegistrationException(message);
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
Debug.Assert(null != builder.Type);
|
||||
|
||||
|
|
|
@ -2,7 +2,6 @@
|
|||
using Unity.Builder;
|
||||
using Unity.Lifetime;
|
||||
using Unity.Resolution;
|
||||
using static Unity.UnityContainer;
|
||||
|
||||
namespace Unity
|
||||
{
|
||||
|
@ -12,31 +11,24 @@ namespace Unity
|
|||
|
||||
public override ResolveDelegate<BuilderContext>? Build(ref PipelineBuilder builder)
|
||||
{
|
||||
if (builder.LifetimeManager is ContainerControlledLifetimeManager manager)
|
||||
{
|
||||
var pipeline = builder.Pipeline();
|
||||
ResolveDelegate<BuilderContext>? pipeline = builder.Pipeline();
|
||||
Debug.Assert(null != pipeline);
|
||||
|
||||
return (ref BuilderContext context) =>
|
||||
return builder.LifetimeManager is SynchronizedLifetimeManager manager ?
|
||||
(ref BuilderContext context) =>
|
||||
{
|
||||
var scope = context.ContainerContext;
|
||||
|
||||
try
|
||||
{
|
||||
// Switch context to lifetime's scope
|
||||
context.ContainerContext = (ContainerContext)manager.Scope;
|
||||
|
||||
// Build withing the scope
|
||||
return pipeline(ref context);
|
||||
}
|
||||
finally
|
||||
catch
|
||||
{
|
||||
context.ContainerContext = scope;
|
||||
manager.Recover();
|
||||
throw;
|
||||
}
|
||||
};
|
||||
}
|
||||
|
||||
return builder.Pipeline(); ;
|
||||
: pipeline;
|
||||
}
|
||||
|
||||
#endregion
|
||||
|
|
|
@ -19,6 +19,7 @@ namespace Unity.Registration
|
|||
{
|
||||
Next = null;
|
||||
Type = type;
|
||||
LifetimeManager = new TransientLifetimeManager();
|
||||
InjectionMembers = null;
|
||||
BuildRequired = false;
|
||||
}
|
||||
|
@ -40,7 +41,7 @@ namespace Unity.Registration
|
|||
: base(owner, name)
|
||||
{
|
||||
Type = type;
|
||||
LifetimeManager = lifetimeManager is TransientLifetimeManager ? null : lifetimeManager;
|
||||
LifetimeManager = lifetimeManager;
|
||||
Next = owner.Defaults;
|
||||
InjectionMembers = null != injectionMembers && 0 < injectionMembers.Length ? injectionMembers : null;
|
||||
BuildRequired = null != InjectionMembers && InjectionMembers.Any(m => m.BuildRequired) || lifetimeManager is PerResolveLifetimeManager;
|
||||
|
|
|
@ -15,7 +15,7 @@ namespace Unity.Registration
|
|||
if (manager is IDisposable managerDisposable)
|
||||
owner.Context.Lifetime.Add(managerDisposable);
|
||||
|
||||
LifetimeManager = manager is TransientLifetimeManager ? null : manager;
|
||||
LifetimeManager = manager;
|
||||
|
||||
// Factory resolver
|
||||
if (manager is PerResolveLifetimeManager)
|
||||
|
@ -23,7 +23,7 @@ namespace Unity.Registration
|
|||
Pipeline = (ref BuilderContext context) =>
|
||||
{
|
||||
var value = factory(context.Container, context.Type, context.Name);
|
||||
context.Set(typeof(LifetimeManager), new InternalPerResolveLifetimeManager(value));
|
||||
context.Set(typeof(LifetimeManager), new RuntimePerResolveLifetimeManager(value));
|
||||
return value;
|
||||
};
|
||||
}
|
||||
|
|
|
@ -19,11 +19,6 @@ namespace Unity.Registration
|
|||
manager.InUse = true;
|
||||
manager.SetValue(instance, owner.Context.Lifetime);
|
||||
Pipeline = (ref BuilderContext context) => throw new InvalidOperationException("Instance value no longer available");
|
||||
//Pipeline = manager switch
|
||||
//{
|
||||
// ExternallyControlledLifetimeManager _ => ExternalLifetime,
|
||||
// _ => (ResolveDelegate<BuilderContext>)OtherLifetime
|
||||
//};
|
||||
|
||||
LifetimeManager = manager;
|
||||
}
|
||||
|
|
|
@ -28,7 +28,7 @@
|
|||
</PropertyGroup>
|
||||
|
||||
<ItemGroup Condition="'$(TargetFramework)' != '' AND '$(TargetFramework)' != 'netstandard1.0' ">
|
||||
<Compile Remove="Utility\ConcurrentDictionary.cs" />
|
||||
<Compile Remove="UnityContainer\Utility\ConcurrentDictionary.cs" />
|
||||
</ItemGroup>
|
||||
|
||||
<PropertyGroup>
|
||||
|
|
|
@ -1,514 +0,0 @@
|
|||
using System;
|
||||
using System.Diagnostics;
|
||||
using System.Reflection;
|
||||
using System.Threading;
|
||||
using Unity.Builder;
|
||||
using Unity.Lifetime;
|
||||
using Unity.Registration;
|
||||
using Unity.Resolution;
|
||||
using Unity.Storage;
|
||||
|
||||
namespace Unity
|
||||
{
|
||||
public partial class UnityContainer
|
||||
{
|
||||
#region Get Pipeline
|
||||
|
||||
private ResolveDelegate<BuilderContext>? TryGetPipeline(ref HashKey key)
|
||||
{
|
||||
// Iterate through containers hierarchy
|
||||
for (UnityContainer? container = this; null != container; container = container._parent)
|
||||
{
|
||||
// Skip to parent if no registrations
|
||||
if (null == container._metadata) continue;
|
||||
|
||||
Debug.Assert(null != container._registry);
|
||||
var registry = container._registry;
|
||||
|
||||
// Check for exact match
|
||||
for (var i = registry.Buckets[key.HashCode % registry.Buckets.Length]; i >= 0; i = registry.Entries[i].Next)
|
||||
{
|
||||
ref var candidate = ref registry.Entries[i];
|
||||
if (candidate.Key != key) continue;
|
||||
|
||||
// Found it
|
||||
return candidate.Pipeline;
|
||||
}
|
||||
}
|
||||
|
||||
return null;
|
||||
}
|
||||
|
||||
internal ResolveDelegate<BuilderContext> GetPipeline(ref HashKey key)
|
||||
{
|
||||
#if NETSTANDARD1_0 || NETCOREAPP1_0
|
||||
var info = key.Type?.GetTypeInfo();
|
||||
return null != info && info.IsGenericType
|
||||
? GenericGetPipeline(ref key, info)
|
||||
: GetNonGenericPipeline(ref key);
|
||||
#else
|
||||
return null != key.Type && key.Type.IsGenericType
|
||||
? GenericGetPipeline(ref key)
|
||||
: GetNonGenericPipeline(ref key);
|
||||
#endif
|
||||
}
|
||||
|
||||
#endregion
|
||||
|
||||
|
||||
#region Implementation
|
||||
|
||||
private ResolveDelegate<BuilderContext> GetNonGenericPipeline(ref HashKey key)
|
||||
{
|
||||
// Iterate through containers hierarchy
|
||||
for (UnityContainer? container = this; null != container; container = container._parent)
|
||||
{
|
||||
// Skip to parent if no registrations
|
||||
if (null == container._metadata) continue;
|
||||
|
||||
Debug.Assert(null != container._registry);
|
||||
var registry = container._registry;
|
||||
|
||||
// Check for exact match
|
||||
for (var i = registry.Buckets[key.HashCode % registry.Buckets.Length]; i >= 0; i = registry.Entries[i].Next)
|
||||
{
|
||||
ref var candidate = ref registry.Entries[i];
|
||||
if (candidate.Key != key) continue;
|
||||
|
||||
// Found it
|
||||
Debug.Assert(null != candidate.Pipeline);
|
||||
return candidate.Pipeline;
|
||||
}
|
||||
}
|
||||
|
||||
Debug.Assert(null != _root);
|
||||
|
||||
return _root.PipelineFromType(ref key);
|
||||
}
|
||||
|
||||
#if NETSTANDARD1_0 || NETCOREAPP1_0
|
||||
private ResolveDelegate<BuilderContext> GenericGetPipeline(ref HashKey key, TypeInfo info)
|
||||
{
|
||||
Debug.Assert(null != info);
|
||||
#else
|
||||
private ResolveDelegate<BuilderContext> GenericGetPipeline(ref HashKey key)
|
||||
{
|
||||
#endif
|
||||
Debug.Assert(null != key.Type);
|
||||
|
||||
int targetBucket;
|
||||
bool initGenerics = true;
|
||||
var keyGeneric = new HashKey();
|
||||
var keyDefault = new HashKey();
|
||||
Type type = key.Type;
|
||||
Type? generic = null;
|
||||
var name = key.Name;
|
||||
|
||||
// Iterate through containers hierarchy
|
||||
for (UnityContainer? container = this; null != container; container = container._parent)
|
||||
{
|
||||
// Skip to parent if no registrations
|
||||
if (null == container._metadata) continue;
|
||||
|
||||
Debug.Assert(null != container._registry);
|
||||
var registry = container._registry;
|
||||
|
||||
// Exact match
|
||||
targetBucket = key.HashCode % registry.Buckets.Length;
|
||||
for (var i = registry.Buckets[targetBucket]; i >= 0; i = registry.Entries[i].Next)
|
||||
{
|
||||
ref var candidate = ref registry.Entries[i];
|
||||
if (candidate.Key != key) continue;
|
||||
|
||||
Debug.Assert(null != candidate.Pipeline);
|
||||
|
||||
// Found a registration
|
||||
return candidate.Pipeline;
|
||||
}
|
||||
|
||||
// Generic registrations
|
||||
if (initGenerics)
|
||||
{
|
||||
initGenerics = false;
|
||||
|
||||
#if NETSTANDARD1_0 || NETCOREAPP1_0
|
||||
generic = info.GetGenericTypeDefinition();
|
||||
#else
|
||||
generic = type.GetGenericTypeDefinition();
|
||||
#endif
|
||||
keyGeneric = new HashKey(generic, name);
|
||||
keyDefault = new HashKey(generic);
|
||||
}
|
||||
|
||||
// Factory with the same name
|
||||
targetBucket = keyGeneric.HashCode % registry.Buckets.Length;
|
||||
for (var i = registry.Buckets[targetBucket]; i >= 0; i = registry.Entries[i].Next)
|
||||
{
|
||||
ref var candidate = ref registry.Entries[i];
|
||||
if (candidate.Key != keyGeneric)
|
||||
continue;
|
||||
|
||||
// Found a factory
|
||||
return container.PipelineFromFactory(ref key, (ExplicitRegistration)candidate.Policies);
|
||||
}
|
||||
|
||||
// Default factory
|
||||
targetBucket = keyDefault.HashCode % registry.Buckets.Length;
|
||||
for (var i = registry.Buckets[targetBucket]; i >= 0; i = registry.Entries[i].Next)
|
||||
{
|
||||
ref var candidate = ref registry.Entries[i];
|
||||
if (candidate.Key != keyDefault)
|
||||
continue;
|
||||
|
||||
// Found a factory
|
||||
var typeFactory = (TypeFactoryDelegate)candidate.Policies.Get(typeof(TypeFactoryDelegate));
|
||||
return container.PipelineFromTypeFactory(ref key, container, typeFactory);
|
||||
}
|
||||
}
|
||||
|
||||
Debug.Assert(null != _root);
|
||||
|
||||
return _root.PipelineFromType(ref key);
|
||||
}
|
||||
|
||||
#endregion
|
||||
|
||||
|
||||
#region Create Pipeline
|
||||
|
||||
private ResolveDelegate<BuilderContext> PipelineFromType(ref HashKey key)
|
||||
{
|
||||
Debug.Assert(null != key.Type);
|
||||
Debug.Assert(null != _registry);
|
||||
|
||||
var type = key.Type;
|
||||
var name = key.Name;
|
||||
int count = -1;
|
||||
var collisions = 0;
|
||||
ResolveDelegate<BuilderContext>? pipeline = null;
|
||||
|
||||
lock (_syncLock)
|
||||
{
|
||||
var targetBucket = key.HashCode % _registry.Buckets.Length;
|
||||
for (var i = _registry.Buckets[targetBucket]; i >= 0; i = _registry.Entries[i].Next)
|
||||
{
|
||||
ref var candidate = ref _registry.Entries[i];
|
||||
if (candidate.Key != key)
|
||||
{
|
||||
collisions++;
|
||||
continue;
|
||||
}
|
||||
|
||||
if (null == candidate.Pipeline)
|
||||
candidate.Pipeline = buildPipeline;
|
||||
|
||||
// Replaced registration
|
||||
return candidate.Pipeline;
|
||||
}
|
||||
|
||||
// Expand if required
|
||||
if (_registry.RequireToGrow || CollisionsCutPoint < collisions)
|
||||
{
|
||||
_registry = new Registry(_registry);
|
||||
targetBucket = key.HashCode % _registry.Buckets.Length;
|
||||
}
|
||||
|
||||
// Create new entry
|
||||
ref var entry = ref _registry.Entries[_registry.Count];
|
||||
entry.Key = key;
|
||||
entry.Next = _registry.Buckets[targetBucket];
|
||||
entry.Type = type;
|
||||
entry.IsExplicit = true;
|
||||
entry.Pipeline = buildPipeline;
|
||||
int position = _registry.Count++;
|
||||
_registry.Buckets[targetBucket] = position;
|
||||
}
|
||||
|
||||
return buildPipeline;
|
||||
|
||||
|
||||
object? buildPipeline(ref BuilderContext context)
|
||||
{
|
||||
// Wait for right moment
|
||||
while (0 != Interlocked.Increment(ref count))
|
||||
{
|
||||
Interlocked.Decrement(ref count);
|
||||
#if NETSTANDARD1_0 || NETCOREAPP1_0
|
||||
for (var i = 0; i < 100; i++) ;
|
||||
#else
|
||||
Thread.SpinWait(100);
|
||||
#endif
|
||||
}
|
||||
|
||||
try
|
||||
{
|
||||
// Create if required
|
||||
if (null == pipeline)
|
||||
{
|
||||
PipelineBuilder builder = new PipelineBuilder(type, this, Context.TypePipelineCache);
|
||||
pipeline = builder.Pipeline();
|
||||
|
||||
Debug.Assert(null != pipeline);
|
||||
}
|
||||
|
||||
return pipeline(ref context);
|
||||
}
|
||||
finally
|
||||
{
|
||||
Interlocked.Decrement(ref count);
|
||||
}
|
||||
};
|
||||
|
||||
}
|
||||
|
||||
private ResolveDelegate<BuilderContext> PipelineFromRegistration(ref HashKey key, ExplicitRegistration registration, int position)
|
||||
{
|
||||
Debug.Assert(null != _registry);
|
||||
|
||||
var type = key.Type;
|
||||
var name = key.Name;
|
||||
ResolveDelegate<BuilderContext>? pipeline = null;
|
||||
|
||||
if (null != registration.LifetimeManager && !(registration.LifetimeManager is TransientLifetimeManager))
|
||||
{
|
||||
registration.LifetimeManager.PipelineDelegate = (ResolveDelegate<BuilderContext>)BuildPipeline;
|
||||
return registration.LifetimeManager.Pipeline;
|
||||
}
|
||||
|
||||
return BuildPipeline;
|
||||
|
||||
object? BuildPipeline(ref BuilderContext context)
|
||||
{
|
||||
if (null != pipeline) return pipeline(ref context);
|
||||
lock (registration)
|
||||
{
|
||||
if (null != pipeline) return pipeline(ref context);
|
||||
|
||||
PipelineBuilder builder = new PipelineBuilder(type, registration);
|
||||
|
||||
if (registration.LifetimeManager is LifetimeManager manager)
|
||||
{
|
||||
manager.PipelineDelegate = builder.Pipeline();
|
||||
pipeline = manager.Pipeline;
|
||||
}
|
||||
else
|
||||
pipeline = builder.Pipeline();
|
||||
|
||||
Debug.Assert(null != pipeline);
|
||||
Debug.Assert(null != _registry);
|
||||
|
||||
lock (_syncLock)
|
||||
{
|
||||
if (ReferenceEquals(registration, _registry.Entries[position].Policies))
|
||||
{
|
||||
_registry.Entries[position].Pipeline = pipeline;
|
||||
}
|
||||
}
|
||||
|
||||
// Check if already created and acquire a lock if not
|
||||
if (pipeline.Target is LifetimeManager lifetime)
|
||||
{
|
||||
// Make blocking check for result
|
||||
var value = lifetime.Get(LifetimeContainer);
|
||||
if (LifetimeManager.NoValue != value) return value;
|
||||
}
|
||||
|
||||
return pipeline(ref context);
|
||||
}
|
||||
};
|
||||
}
|
||||
|
||||
private ResolveDelegate<BuilderContext> PipelineFromFactory(ref HashKey key, ExplicitRegistration factory)
|
||||
{
|
||||
Debug.Assert(null != _registry);
|
||||
Debug.Assert(null != key.Type);
|
||||
|
||||
var type = key.Type;
|
||||
var name = key.Name;
|
||||
var owner = this;
|
||||
|
||||
int count = -1;
|
||||
int position = 0;
|
||||
var collisions = 0;
|
||||
ResolveDelegate<BuilderContext>? pipeline = null;
|
||||
ResolveDelegate<BuilderContext> buildPipeline;
|
||||
var manager = null != factory.LifetimeManager && !(factory.LifetimeManager is TransientLifetimeManager)
|
||||
? factory.LifetimeManager.CreateLifetimePolicy()
|
||||
: null;
|
||||
|
||||
if (null != manager)
|
||||
{
|
||||
if (manager is IDisposable disposable) Context.Lifetime.Add(disposable);
|
||||
if (manager is ContainerControlledLifetimeManager containerControlled) containerControlled.Scope = Context;
|
||||
|
||||
manager.PipelineDelegate = (ResolveDelegate<BuilderContext>)BuildPipeline;
|
||||
buildPipeline = manager.Pipeline;
|
||||
}
|
||||
else
|
||||
buildPipeline = BuildPipeline;
|
||||
|
||||
// Add Pipeline to the Registry
|
||||
lock (_syncLock)
|
||||
{
|
||||
var targetBucket = key.HashCode % _registry.Buckets.Length;
|
||||
for (var i = _registry.Buckets[targetBucket]; i >= 0; i = _registry.Entries[i].Next)
|
||||
{
|
||||
ref var candidate = ref _registry.Entries[i];
|
||||
if (candidate.Key != key)
|
||||
{
|
||||
collisions++;
|
||||
continue;
|
||||
}
|
||||
|
||||
Debug.Assert(null != candidate.Pipeline);
|
||||
|
||||
// Has already been created
|
||||
return candidate.Pipeline;
|
||||
}
|
||||
|
||||
// Expand if required
|
||||
if (_registry.RequireToGrow || CollisionsCutPoint < collisions)
|
||||
{
|
||||
_registry = new Registry(_registry);
|
||||
targetBucket = key.HashCode % _registry.Buckets.Length;
|
||||
}
|
||||
|
||||
// Create new entry
|
||||
ref var entry = ref _registry.Entries[_registry.Count];
|
||||
entry.Key = key;
|
||||
entry.Type = type;
|
||||
entry.Pipeline = buildPipeline;
|
||||
entry.Next = _registry.Buckets[targetBucket];
|
||||
position = _registry.Count++;
|
||||
_registry.Buckets[targetBucket] = position;
|
||||
}
|
||||
|
||||
// Return temporary pipeline
|
||||
return buildPipeline;
|
||||
|
||||
|
||||
// Create pipeline and add to Registry
|
||||
object? BuildPipeline(ref BuilderContext context)
|
||||
{
|
||||
// Wait for right moment
|
||||
while (0 != Interlocked.Increment(ref count))
|
||||
{
|
||||
Interlocked.Decrement(ref count);
|
||||
#if NETSTANDARD1_0 || NETCOREAPP1_0
|
||||
for (var i = 0; i < 100; i++) ;
|
||||
#else
|
||||
Thread.SpinWait(100);
|
||||
#endif
|
||||
}
|
||||
|
||||
try
|
||||
{
|
||||
// Create if required
|
||||
if (null == pipeline)
|
||||
{
|
||||
PipelineBuilder builder = new PipelineBuilder(type, factory, manager, owner);
|
||||
pipeline = builder.Pipeline();
|
||||
if (null != manager) manager.PipelineDelegate = pipeline;
|
||||
Debug.Assert(null != pipeline);
|
||||
}
|
||||
|
||||
var value = pipeline(ref context);
|
||||
manager?.Set(value, LifetimeContainer);
|
||||
return value;
|
||||
}
|
||||
finally
|
||||
{
|
||||
Interlocked.Decrement(ref count);
|
||||
}
|
||||
};
|
||||
}
|
||||
|
||||
private ResolveDelegate<BuilderContext> PipelineFromTypeFactory(ref HashKey key, UnityContainer container, TypeFactoryDelegate typeFactory)
|
||||
{
|
||||
Debug.Assert(null != _registry);
|
||||
Debug.Assert(null != key.Type);
|
||||
|
||||
var type = key.Type;
|
||||
var name = key.Name;
|
||||
var owner = this;
|
||||
|
||||
int count = -1;
|
||||
int position = 0;
|
||||
var collisions = 0;
|
||||
ResolveDelegate<BuilderContext>? pipeline = null;
|
||||
|
||||
// Add Pipeline to the Registry
|
||||
lock (_syncLock)
|
||||
{
|
||||
var targetBucket = key.HashCode % _registry.Buckets.Length;
|
||||
for (var i = _registry.Buckets[targetBucket]; i >= 0; i = _registry.Entries[i].Next)
|
||||
{
|
||||
ref var candidate = ref _registry.Entries[i];
|
||||
if (candidate.Key != key)
|
||||
{
|
||||
collisions++;
|
||||
continue;
|
||||
}
|
||||
|
||||
Debug.Assert(null != candidate.Pipeline);
|
||||
|
||||
// Has already been created
|
||||
return candidate.Pipeline;
|
||||
}
|
||||
|
||||
// Expand if required
|
||||
if (_registry.RequireToGrow || CollisionsCutPoint < collisions)
|
||||
{
|
||||
_registry = new Registry(_registry);
|
||||
targetBucket = key.HashCode % _registry.Buckets.Length;
|
||||
}
|
||||
|
||||
// Create new entry
|
||||
ref var entry = ref _registry.Entries[_registry.Count];
|
||||
entry.Key = key;
|
||||
entry.Type = type;
|
||||
entry.Pipeline = BuildPipeline;
|
||||
entry.Next = _registry.Buckets[targetBucket];
|
||||
position = _registry.Count++;
|
||||
_registry.Buckets[targetBucket] = position;
|
||||
}
|
||||
|
||||
// Return temporary pipeline
|
||||
return BuildPipeline;
|
||||
|
||||
|
||||
// Create pipeline and add to Registry
|
||||
object? BuildPipeline(ref BuilderContext context)
|
||||
{
|
||||
// Wait for right moment
|
||||
while (0 != Interlocked.Increment(ref count))
|
||||
{
|
||||
Interlocked.Decrement(ref count);
|
||||
#if NETSTANDARD1_0 || NETCOREAPP1_0
|
||||
for (var i = 0; i < 100; i++) ;
|
||||
#else
|
||||
Thread.SpinWait(100);
|
||||
#endif
|
||||
}
|
||||
|
||||
try
|
||||
{
|
||||
// Create if required
|
||||
if (null == pipeline)
|
||||
{
|
||||
pipeline = typeFactory(type, container);
|
||||
Debug.Assert(null != pipeline);
|
||||
}
|
||||
|
||||
return pipeline(ref context);
|
||||
}
|
||||
finally
|
||||
{
|
||||
Interlocked.Decrement(ref count);
|
||||
}
|
||||
};
|
||||
}
|
||||
|
||||
#endregion
|
||||
}
|
||||
}
|
|
@ -219,7 +219,6 @@ namespace Unity
|
|||
}
|
||||
|
||||
// Setup Context
|
||||
var synchronized = manager as SynchronizedLifetimeManager;
|
||||
var context = new BuilderContext
|
||||
{
|
||||
List = new PolicyList(),
|
||||
|
@ -237,18 +236,11 @@ namespace Unity
|
|||
return value;
|
||||
}
|
||||
catch (Exception ex)
|
||||
{
|
||||
synchronized?.Recover();
|
||||
|
||||
if (ex is InvalidRegistrationException ||
|
||||
ex is CircularDependencyException ||
|
||||
ex is ObjectDisposedException)
|
||||
when (ex is InvalidRegistrationException || ex is CircularDependencyException || ex is ObjectDisposedException)
|
||||
{
|
||||
var message = CreateErrorMessage(ex);
|
||||
throw new ResolutionFailedException(context.Type, context.Name, message, ex);
|
||||
}
|
||||
else throw;
|
||||
}
|
||||
}
|
||||
|
||||
#endregion
|
|
@ -284,24 +284,5 @@ namespace Unity
|
|||
}
|
||||
|
||||
#endregion
|
||||
|
||||
|
||||
private class ContainerLifetimeManager : LifetimeManager
|
||||
{
|
||||
protected override LifetimeManager OnCreateLifetimeManager()
|
||||
=> throw new NotImplementedException();
|
||||
|
||||
public override object GetValue(ILifetimeContainer container)
|
||||
{
|
||||
Debug.Assert(null != container);
|
||||
return container.Container;
|
||||
}
|
||||
|
||||
public override object TryGetValue(ILifetimeContainer container)
|
||||
{
|
||||
Debug.Assert(null != container);
|
||||
return container.Container;
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
|
@ -0,0 +1,174 @@
|
|||
using System;
|
||||
using System.Diagnostics;
|
||||
using System.Reflection;
|
||||
using Unity.Builder;
|
||||
using Unity.Registration;
|
||||
using Unity.Resolution;
|
||||
using Unity.Storage;
|
||||
|
||||
namespace Unity
|
||||
{
|
||||
public partial class UnityContainer
|
||||
{
|
||||
#region Get Pipeline
|
||||
|
||||
private ResolveDelegate<BuilderContext>? TryGetPipeline(ref HashKey key)
|
||||
{
|
||||
// Iterate through containers hierarchy
|
||||
for (UnityContainer? container = this; null != container; container = container._parent)
|
||||
{
|
||||
// Skip to parent if no registrations
|
||||
if (null == container._metadata) continue;
|
||||
|
||||
Debug.Assert(null != container._registry);
|
||||
var registry = container._registry;
|
||||
|
||||
// Check for exact match
|
||||
for (var i = registry.Buckets[key.HashCode % registry.Buckets.Length]; i >= 0; i = registry.Entries[i].Next)
|
||||
{
|
||||
ref var candidate = ref registry.Entries[i];
|
||||
if (candidate.Key != key) continue;
|
||||
|
||||
// Found it
|
||||
return candidate.Pipeline;
|
||||
}
|
||||
}
|
||||
|
||||
return null;
|
||||
}
|
||||
|
||||
internal ResolveDelegate<BuilderContext> GetPipeline(ref HashKey key)
|
||||
{
|
||||
#if NETSTANDARD1_0 || NETCOREAPP1_0
|
||||
var info = key.Type?.GetTypeInfo();
|
||||
return null != info && info.IsGenericType
|
||||
? GenericGetPipeline(ref key, info)
|
||||
: GetNonGenericPipeline(ref key);
|
||||
#else
|
||||
return null != key.Type && key.Type.IsGenericType
|
||||
? GenericGetPipeline(ref key)
|
||||
: GetNonGenericPipeline(ref key);
|
||||
#endif
|
||||
}
|
||||
|
||||
#endregion
|
||||
|
||||
|
||||
#region Implementation
|
||||
|
||||
private ResolveDelegate<BuilderContext> GetNonGenericPipeline(ref HashKey key)
|
||||
{
|
||||
// Iterate through containers hierarchy
|
||||
for (UnityContainer? container = this; null != container; container = container._parent)
|
||||
{
|
||||
// Skip to parent if no registrations
|
||||
if (null == container._metadata) continue;
|
||||
|
||||
Debug.Assert(null != container._registry);
|
||||
var registry = container._registry;
|
||||
|
||||
// Check for exact match
|
||||
for (var i = registry.Buckets[key.HashCode % registry.Buckets.Length]; i >= 0; i = registry.Entries[i].Next)
|
||||
{
|
||||
ref var candidate = ref registry.Entries[i];
|
||||
if (candidate.Key != key) continue;
|
||||
|
||||
// Found it
|
||||
Debug.Assert(null != candidate.Pipeline);
|
||||
return candidate.Pipeline;
|
||||
}
|
||||
}
|
||||
|
||||
Debug.Assert(null != _root);
|
||||
|
||||
return _root.PipelineFromUnregisteredType(ref key);
|
||||
}
|
||||
|
||||
#if NETSTANDARD1_0 || NETCOREAPP1_0
|
||||
private ResolveDelegate<BuilderContext> GenericGetPipeline(ref HashKey key, TypeInfo info)
|
||||
{
|
||||
Debug.Assert(null != info);
|
||||
#else
|
||||
private ResolveDelegate<BuilderContext> GenericGetPipeline(ref HashKey key)
|
||||
{
|
||||
#endif
|
||||
Debug.Assert(null != key.Type);
|
||||
|
||||
int targetBucket;
|
||||
bool initGenerics = true;
|
||||
var keyGeneric = new HashKey();
|
||||
var keyDefault = new HashKey();
|
||||
Type type = key.Type;
|
||||
Type? generic = null;
|
||||
var name = key.Name;
|
||||
|
||||
// Iterate through containers hierarchy
|
||||
for (UnityContainer? container = this; null != container; container = container._parent)
|
||||
{
|
||||
// Skip to parent if no registrations
|
||||
if (null == container._metadata) continue;
|
||||
|
||||
Debug.Assert(null != container._registry);
|
||||
var registry = container._registry;
|
||||
|
||||
// Exact match
|
||||
targetBucket = key.HashCode % registry.Buckets.Length;
|
||||
for (var i = registry.Buckets[targetBucket]; i >= 0; i = registry.Entries[i].Next)
|
||||
{
|
||||
ref var candidate = ref registry.Entries[i];
|
||||
if (candidate.Key != key) continue;
|
||||
|
||||
Debug.Assert(null != candidate.Pipeline);
|
||||
|
||||
// Found a registration
|
||||
return candidate.Pipeline;
|
||||
}
|
||||
|
||||
// Generic registrations
|
||||
if (initGenerics)
|
||||
{
|
||||
initGenerics = false;
|
||||
|
||||
#if NETSTANDARD1_0 || NETCOREAPP1_0
|
||||
generic = info.GetGenericTypeDefinition();
|
||||
#else
|
||||
generic = type.GetGenericTypeDefinition();
|
||||
#endif
|
||||
keyGeneric = new HashKey(generic, name);
|
||||
keyDefault = new HashKey(generic);
|
||||
}
|
||||
|
||||
// Factory with the same name
|
||||
targetBucket = keyGeneric.HashCode % registry.Buckets.Length;
|
||||
for (var i = registry.Buckets[targetBucket]; i >= 0; i = registry.Entries[i].Next)
|
||||
{
|
||||
ref var candidate = ref registry.Entries[i];
|
||||
if (candidate.Key != keyGeneric)
|
||||
continue;
|
||||
|
||||
// Found a factory
|
||||
return container.PipelineFromOpenGeneric(ref key, (ExplicitRegistration)candidate.Policies);
|
||||
}
|
||||
|
||||
// Default factory
|
||||
targetBucket = keyDefault.HashCode % registry.Buckets.Length;
|
||||
for (var i = registry.Buckets[targetBucket]; i >= 0; i = registry.Entries[i].Next)
|
||||
{
|
||||
ref var candidate = ref registry.Entries[i];
|
||||
if (candidate.Key != keyDefault)
|
||||
continue;
|
||||
|
||||
// Found a factory
|
||||
var typeFactory = (TypeFactoryDelegate)candidate.Policies.Get(typeof(TypeFactoryDelegate));
|
||||
return container.PipelineFromTypeFactory(ref key, container, typeFactory);
|
||||
}
|
||||
}
|
||||
|
||||
Debug.Assert(null != _root);
|
||||
|
||||
return _root.PipelineFromUnregisteredType(ref key);
|
||||
}
|
||||
|
||||
#endregion
|
||||
}
|
||||
}
|
|
@ -0,0 +1,119 @@
|
|||
using System;
|
||||
using System.Diagnostics;
|
||||
using System.Threading;
|
||||
using Unity.Builder;
|
||||
using Unity.Lifetime;
|
||||
using Unity.Registration;
|
||||
using Unity.Resolution;
|
||||
using Unity.Storage;
|
||||
|
||||
namespace Unity
|
||||
{
|
||||
public partial class UnityContainer
|
||||
{
|
||||
private ResolveDelegate<BuilderContext> PipelineFromOpenGeneric(ref HashKey key, ExplicitRegistration factory)
|
||||
{
|
||||
Debug.Assert(null != _registry);
|
||||
Debug.Assert(null != key.Type);
|
||||
|
||||
var type = key.Type;
|
||||
var name = key.Name;
|
||||
var owner = this;
|
||||
|
||||
int count = -1;
|
||||
int position = 0;
|
||||
var collisions = 0;
|
||||
ResolveDelegate<BuilderContext>? pipeline = null;
|
||||
ResolveDelegate<BuilderContext> buildPipeline;
|
||||
var manager = null != factory.LifetimeManager && !(factory.LifetimeManager is TransientLifetimeManager)
|
||||
? factory.LifetimeManager.CreateLifetimePolicy()
|
||||
: null;
|
||||
|
||||
if (null != manager)
|
||||
{
|
||||
if (manager is IDisposable disposable) Context.Lifetime.Add(disposable);
|
||||
if (manager is ContainerControlledLifetimeManager containerControlled) containerControlled.Scope = Context;
|
||||
|
||||
manager.PipelineDelegate = (ResolveDelegate<BuilderContext>)BuildPipeline;
|
||||
buildPipeline = manager.Pipeline;
|
||||
}
|
||||
else
|
||||
buildPipeline = BuildPipeline;
|
||||
|
||||
// Add Pipeline to the Registry
|
||||
lock (_syncLock)
|
||||
{
|
||||
var targetBucket = key.HashCode % _registry.Buckets.Length;
|
||||
for (var i = _registry.Buckets[targetBucket]; i >= 0; i = _registry.Entries[i].Next)
|
||||
{
|
||||
ref var candidate = ref _registry.Entries[i];
|
||||
if (candidate.Key != key)
|
||||
{
|
||||
collisions++;
|
||||
continue;
|
||||
}
|
||||
|
||||
Debug.Assert(null != candidate.Pipeline);
|
||||
|
||||
// Has already been created
|
||||
return candidate.Pipeline;
|
||||
}
|
||||
|
||||
// Expand if required
|
||||
if (_registry.RequireToGrow || CollisionsCutPoint < collisions)
|
||||
{
|
||||
_registry = new Registry(_registry);
|
||||
targetBucket = key.HashCode % _registry.Buckets.Length;
|
||||
}
|
||||
|
||||
// Create new entry
|
||||
ref var entry = ref _registry.Entries[_registry.Count];
|
||||
entry.Key = key;
|
||||
entry.Type = type;
|
||||
entry.Pipeline = buildPipeline;
|
||||
entry.Next = _registry.Buckets[targetBucket];
|
||||
position = _registry.Count++;
|
||||
_registry.Buckets[targetBucket] = position;
|
||||
}
|
||||
|
||||
// Return temporary pipeline
|
||||
return buildPipeline;
|
||||
|
||||
|
||||
// Create pipeline and add to Registry
|
||||
object? BuildPipeline(ref BuilderContext context)
|
||||
{
|
||||
// Wait for right moment
|
||||
while (0 != Interlocked.Increment(ref count))
|
||||
{
|
||||
Interlocked.Decrement(ref count);
|
||||
#if NETSTANDARD1_0 || NETCOREAPP1_0
|
||||
for (var i = 0; i < 100; i++) ;
|
||||
#else
|
||||
Thread.SpinWait(100);
|
||||
#endif
|
||||
}
|
||||
|
||||
try
|
||||
{
|
||||
// Create if required
|
||||
if (null == pipeline)
|
||||
{
|
||||
PipelineBuilder builder = new PipelineBuilder(type, factory, manager, owner);
|
||||
pipeline = builder.Pipeline();
|
||||
if (null != manager) manager.PipelineDelegate = pipeline;
|
||||
Debug.Assert(null != pipeline);
|
||||
}
|
||||
|
||||
var value = pipeline(ref context);
|
||||
manager?.Set(value, LifetimeContainer);
|
||||
return value;
|
||||
}
|
||||
finally
|
||||
{
|
||||
Interlocked.Decrement(ref count);
|
||||
}
|
||||
};
|
||||
}
|
||||
}
|
||||
}
|
|
@ -0,0 +1,186 @@
|
|||
using System;
|
||||
using System.Diagnostics;
|
||||
using Unity.Builder;
|
||||
using Unity.Lifetime;
|
||||
using Unity.Registration;
|
||||
using Unity.Resolution;
|
||||
using Unity.Storage;
|
||||
|
||||
namespace Unity
|
||||
{
|
||||
public partial class UnityContainer
|
||||
{
|
||||
private ResolveDelegate<BuilderContext> PipelineFromRegistration(ref HashKey key, ExplicitRegistration registration, int position)
|
||||
{
|
||||
Debug.Assert(null != _registry);
|
||||
Debug.Assert(null != key.Type);
|
||||
Debug.Assert(null != registration.LifetimeManager);
|
||||
|
||||
registration.LifetimeManager.PipelineDelegate = registration.LifetimeManager switch
|
||||
{
|
||||
TransientLifetimeManager _ => PipelineFromRegistrationTransient(key.Type, registration, position),
|
||||
SynchronizedLifetimeManager _ => PipelineFromRegistrationSynchronized(key.Type, registration),
|
||||
PerResolveLifetimeManager _ => PipelineFromRegistrationPerResolve(key.Type, registration),
|
||||
_ => PipelineFromRegistrationDefault(key.Type, registration)
|
||||
};
|
||||
|
||||
return registration.LifetimeManager.Pipeline;
|
||||
}
|
||||
|
||||
private ResolveDelegate<BuilderContext> PipelineFromRegistrationTransient(Type type, ExplicitRegistration registration, int position)
|
||||
{
|
||||
ResolveDelegate<BuilderContext>? pipeline = null;
|
||||
|
||||
return (ref BuilderContext context) =>
|
||||
{
|
||||
if (null != pipeline) return pipeline(ref context);
|
||||
|
||||
lock (registration)
|
||||
{
|
||||
if (null == pipeline)
|
||||
{
|
||||
PipelineBuilder builder = new PipelineBuilder(type, registration);
|
||||
|
||||
pipeline = builder.Pipeline();
|
||||
|
||||
Debug.Assert(null != pipeline);
|
||||
Debug.Assert(null != _registry);
|
||||
|
||||
lock (_syncLock)
|
||||
{
|
||||
_registry.Entries[position].Pipeline = pipeline;
|
||||
}
|
||||
}
|
||||
|
||||
return pipeline(ref context);
|
||||
}
|
||||
};
|
||||
}
|
||||
|
||||
private ResolveDelegate<BuilderContext> PipelineFromRegistrationSynchronized(Type type, ExplicitRegistration registration)
|
||||
{
|
||||
ResolveDelegate<BuilderContext>? pipeline = null;
|
||||
var manager = registration.LifetimeManager as SynchronizedLifetimeManager;
|
||||
Debug.Assert(null != manager);
|
||||
|
||||
return (ref BuilderContext context) =>
|
||||
{
|
||||
object value;
|
||||
|
||||
if (null != pipeline)
|
||||
{
|
||||
value = manager.Get(LifetimeContainer);
|
||||
if (LifetimeManager.NoValue != value) return value;
|
||||
return pipeline(ref context);
|
||||
}
|
||||
|
||||
lock (registration)
|
||||
{
|
||||
if (null == pipeline)
|
||||
{
|
||||
PipelineBuilder builder = new PipelineBuilder(type, registration);
|
||||
|
||||
pipeline = builder.Pipeline();
|
||||
manager.PipelineDelegate = pipeline;
|
||||
|
||||
Debug.Assert(null != pipeline);
|
||||
}
|
||||
}
|
||||
|
||||
value = manager.Get(LifetimeContainer);
|
||||
if (LifetimeManager.NoValue != value) return value;
|
||||
|
||||
try
|
||||
{
|
||||
// Build withing the scope
|
||||
return pipeline(ref context);
|
||||
}
|
||||
catch
|
||||
{
|
||||
manager.Recover();
|
||||
throw;
|
||||
}
|
||||
};
|
||||
}
|
||||
|
||||
private ResolveDelegate<BuilderContext> PipelineFromRegistrationPerResolve(Type type, ExplicitRegistration registration)
|
||||
{
|
||||
ResolveDelegate<BuilderContext>? pipeline = null;
|
||||
Debug.Assert(null != registration.LifetimeManager);
|
||||
|
||||
return (ref BuilderContext context) =>
|
||||
{
|
||||
object value;
|
||||
LifetimeManager? lifetime;
|
||||
|
||||
if (null != pipeline)
|
||||
{
|
||||
if (null != (lifetime = (LifetimeManager?)context.Get(typeof(LifetimeManager))))
|
||||
{
|
||||
value = lifetime.Get(LifetimeContainer);
|
||||
if (LifetimeManager.NoValue != value) return value;
|
||||
}
|
||||
|
||||
return pipeline(ref context);
|
||||
}
|
||||
|
||||
lock (registration)
|
||||
{
|
||||
if (null == pipeline)
|
||||
{
|
||||
PipelineBuilder builder = new PipelineBuilder(type, registration);
|
||||
|
||||
pipeline = builder.Pipeline();
|
||||
registration.LifetimeManager.PipelineDelegate = pipeline;
|
||||
|
||||
Debug.Assert(null != pipeline);
|
||||
}
|
||||
}
|
||||
|
||||
if (null != (lifetime = (LifetimeManager?)context.Get(typeof(LifetimeManager))))
|
||||
{
|
||||
value = lifetime.Get(LifetimeContainer);
|
||||
if (LifetimeManager.NoValue != value) return value;
|
||||
}
|
||||
|
||||
return pipeline(ref context);
|
||||
};
|
||||
}
|
||||
|
||||
private ResolveDelegate<BuilderContext> PipelineFromRegistrationDefault(Type type, ExplicitRegistration registration)
|
||||
{
|
||||
ResolveDelegate<BuilderContext>? pipeline = null;
|
||||
Debug.Assert(null != registration.LifetimeManager);
|
||||
|
||||
return (ref BuilderContext context) =>
|
||||
{
|
||||
object value;
|
||||
|
||||
if (null != pipeline)
|
||||
{
|
||||
value = registration.LifetimeManager.Get(LifetimeContainer);
|
||||
if (LifetimeManager.NoValue != value) return value;
|
||||
return pipeline(ref context);
|
||||
}
|
||||
|
||||
lock (registration)
|
||||
{
|
||||
if (null == pipeline)
|
||||
{
|
||||
PipelineBuilder builder = new PipelineBuilder(type, registration);
|
||||
|
||||
pipeline = builder.Pipeline();
|
||||
registration.LifetimeManager.PipelineDelegate = pipeline;
|
||||
|
||||
Debug.Assert(null != pipeline);
|
||||
}
|
||||
}
|
||||
|
||||
value = registration.LifetimeManager.Get(LifetimeContainer);
|
||||
if (LifetimeManager.NoValue != value) return value;
|
||||
|
||||
return pipeline(ref context);
|
||||
};
|
||||
}
|
||||
}
|
||||
}
|
|
@ -0,0 +1,97 @@
|
|||
using System.Diagnostics;
|
||||
using System.Threading;
|
||||
using Unity.Builder;
|
||||
using Unity.Resolution;
|
||||
using Unity.Storage;
|
||||
|
||||
namespace Unity
|
||||
{
|
||||
public partial class UnityContainer
|
||||
{
|
||||
private ResolveDelegate<BuilderContext> PipelineFromTypeFactory(ref HashKey key, UnityContainer container, TypeFactoryDelegate typeFactory)
|
||||
{
|
||||
Debug.Assert(null != _registry);
|
||||
Debug.Assert(null != key.Type);
|
||||
|
||||
var type = key.Type;
|
||||
var name = key.Name;
|
||||
var owner = this;
|
||||
|
||||
int count = -1;
|
||||
int position = 0;
|
||||
var collisions = 0;
|
||||
ResolveDelegate<BuilderContext>? pipeline = null;
|
||||
|
||||
// Add Pipeline to the Registry
|
||||
lock (_syncLock)
|
||||
{
|
||||
var targetBucket = key.HashCode % _registry.Buckets.Length;
|
||||
for (var i = _registry.Buckets[targetBucket]; i >= 0; i = _registry.Entries[i].Next)
|
||||
{
|
||||
ref var candidate = ref _registry.Entries[i];
|
||||
if (candidate.Key != key)
|
||||
{
|
||||
collisions++;
|
||||
continue;
|
||||
}
|
||||
|
||||
Debug.Assert(null != candidate.Pipeline);
|
||||
|
||||
// Has already been created
|
||||
return candidate.Pipeline;
|
||||
}
|
||||
|
||||
// Expand if required
|
||||
if (_registry.RequireToGrow || CollisionsCutPoint < collisions)
|
||||
{
|
||||
_registry = new Registry(_registry);
|
||||
targetBucket = key.HashCode % _registry.Buckets.Length;
|
||||
}
|
||||
|
||||
// Create new entry
|
||||
ref var entry = ref _registry.Entries[_registry.Count];
|
||||
entry.Key = key;
|
||||
entry.Type = type;
|
||||
entry.Pipeline = BuildPipeline;
|
||||
entry.Next = _registry.Buckets[targetBucket];
|
||||
position = _registry.Count++;
|
||||
_registry.Buckets[targetBucket] = position;
|
||||
}
|
||||
|
||||
// Return temporary pipeline
|
||||
return BuildPipeline;
|
||||
|
||||
|
||||
// Create pipeline and add to Registry
|
||||
object? BuildPipeline(ref BuilderContext context)
|
||||
{
|
||||
// Wait for right moment
|
||||
while (0 != Interlocked.Increment(ref count))
|
||||
{
|
||||
Interlocked.Decrement(ref count);
|
||||
#if NETSTANDARD1_0 || NETCOREAPP1_0
|
||||
for (var i = 0; i < 100; i++) ;
|
||||
#else
|
||||
Thread.SpinWait(100);
|
||||
#endif
|
||||
}
|
||||
|
||||
try
|
||||
{
|
||||
// Create if required
|
||||
if (null == pipeline)
|
||||
{
|
||||
pipeline = typeFactory(type, container);
|
||||
Debug.Assert(null != pipeline);
|
||||
}
|
||||
|
||||
return pipeline(ref context);
|
||||
}
|
||||
finally
|
||||
{
|
||||
Interlocked.Decrement(ref count);
|
||||
}
|
||||
};
|
||||
}
|
||||
}
|
||||
}
|
|
@ -0,0 +1,172 @@
|
|||
using System;
|
||||
using System.Diagnostics;
|
||||
using Unity.Builder;
|
||||
using Unity.Lifetime;
|
||||
using Unity.Resolution;
|
||||
using Unity.Storage;
|
||||
|
||||
namespace Unity
|
||||
{
|
||||
public partial class UnityContainer
|
||||
{
|
||||
private ResolveDelegate<BuilderContext> PipelineFromUnregisteredType(ref HashKey key)
|
||||
{
|
||||
Debug.Assert(null != _registry);
|
||||
Debug.Assert(null != key.Type);
|
||||
|
||||
LifetimeManager manager;
|
||||
|
||||
lock (_syncLock)
|
||||
{
|
||||
|
||||
var collisions = 0;
|
||||
var targetBucket = key.HashCode % _registry.Buckets.Length;
|
||||
for (var i = _registry.Buckets[targetBucket]; i >= 0; i = _registry.Entries[i].Next)
|
||||
{
|
||||
ref var candidate = ref _registry.Entries[i];
|
||||
if (candidate.Key != key)
|
||||
{
|
||||
collisions++;
|
||||
continue;
|
||||
}
|
||||
|
||||
// TODO: Check if null pipeline is possible
|
||||
if (null == candidate.Pipeline)
|
||||
{
|
||||
// Create or get Lifetime Manager
|
||||
manager = Context.TypeLifetimeManager.CreateLifetimePolicy();
|
||||
manager.PipelineDelegate = manager switch
|
||||
{
|
||||
TransientLifetimeManager transient => PipelineFromUnregisteredTypeTransient(key.Type, transient, _registry.Count),
|
||||
PerResolveLifetimeManager peresolve => PipelineFromUnregisteredTypePerResolve(key.Type, peresolve),
|
||||
_ => PipelineFromUnregisteredTypeDefault(key.Type, manager)
|
||||
};
|
||||
|
||||
candidate.Pipeline = manager.Pipeline;
|
||||
}
|
||||
|
||||
// Replaced registration
|
||||
return candidate.Pipeline;
|
||||
}
|
||||
|
||||
// Expand if required
|
||||
if (_registry.RequireToGrow || CollisionsCutPoint < collisions)
|
||||
{
|
||||
_registry = new Registry(_registry);
|
||||
targetBucket = key.HashCode % _registry.Buckets.Length;
|
||||
}
|
||||
|
||||
|
||||
// Create a Lifetime Manager
|
||||
manager = Context.TypeLifetimeManager.CreateLifetimePolicy();
|
||||
manager.PipelineDelegate = manager switch
|
||||
{
|
||||
TransientLifetimeManager transient => PipelineFromUnregisteredTypeTransient(key.Type, transient, _registry.Count),
|
||||
PerResolveLifetimeManager peresolve => PipelineFromUnregisteredTypePerResolve(key.Type, peresolve),
|
||||
_ => PipelineFromUnregisteredTypeDefault(key.Type, manager)
|
||||
};
|
||||
|
||||
Debug.Assert(null != key.Type);
|
||||
|
||||
// Create new entry
|
||||
ref var entry = ref _registry.Entries[_registry.Count];
|
||||
entry.Key = key;
|
||||
entry.Next = _registry.Buckets[targetBucket];
|
||||
entry.Type = key.Type;
|
||||
entry.IsExplicit = true;
|
||||
entry.Pipeline = manager.Pipeline;
|
||||
int position = _registry.Count++;
|
||||
_registry.Buckets[targetBucket] = position;
|
||||
}
|
||||
|
||||
return manager.Pipeline;
|
||||
}
|
||||
|
||||
private ResolveDelegate<BuilderContext> PipelineFromUnregisteredTypeTransient(Type type, TransientLifetimeManager manager, int position)
|
||||
{
|
||||
ResolveDelegate<BuilderContext>? pipeline = null;
|
||||
|
||||
return (ref BuilderContext context) =>
|
||||
{
|
||||
lock (manager)
|
||||
{
|
||||
// Create if required
|
||||
if (null == pipeline)
|
||||
{
|
||||
PipelineBuilder builder = new PipelineBuilder(type, this, Context.TypePipelineCache);
|
||||
pipeline = builder.Pipeline();
|
||||
|
||||
Debug.Assert(null != pipeline);
|
||||
Debug.Assert(null != _registry);
|
||||
|
||||
// Replace pipeline in storage
|
||||
lock (_syncLock)
|
||||
{
|
||||
_registry.Entries[position].Pipeline = pipeline;
|
||||
}
|
||||
}
|
||||
|
||||
return pipeline(ref context);
|
||||
}
|
||||
};
|
||||
}
|
||||
|
||||
private ResolveDelegate<BuilderContext> PipelineFromUnregisteredTypePerResolve(Type type, PerResolveLifetimeManager manager)
|
||||
{
|
||||
ResolveDelegate<BuilderContext>? pipeline = null;
|
||||
|
||||
return (ref BuilderContext context) =>
|
||||
{
|
||||
lock (manager)
|
||||
{
|
||||
// Create if required
|
||||
if (null == pipeline)
|
||||
{
|
||||
PipelineBuilder builder = new PipelineBuilder(type, this, Context.TypePipelineCache);
|
||||
pipeline = builder.Pipeline();
|
||||
|
||||
Debug.Assert(null != pipeline);
|
||||
|
||||
manager.PipelineDelegate = pipeline;
|
||||
return pipeline(ref context);
|
||||
}
|
||||
|
||||
var lifetime = (LifetimeManager?)context.Get(typeof(LifetimeManager));
|
||||
Debug.Assert(null != lifetime);
|
||||
|
||||
var value = lifetime.Get(LifetimeContainer);
|
||||
if (LifetimeManager.NoValue != value) return value;
|
||||
|
||||
return pipeline(ref context);
|
||||
}
|
||||
};
|
||||
}
|
||||
|
||||
private ResolveDelegate<BuilderContext> PipelineFromUnregisteredTypeDefault(Type type, LifetimeManager manager)
|
||||
{
|
||||
ResolveDelegate<BuilderContext>? pipeline = null;
|
||||
|
||||
return (ref BuilderContext context) =>
|
||||
{
|
||||
lock (manager)
|
||||
{
|
||||
// Create if required
|
||||
if (null == pipeline)
|
||||
{
|
||||
PipelineBuilder builder = new PipelineBuilder(type, this, Context.TypePipelineCache);
|
||||
pipeline = builder.Pipeline();
|
||||
|
||||
Debug.Assert(null != pipeline);
|
||||
|
||||
manager.PipelineDelegate = pipeline;
|
||||
}
|
||||
|
||||
var value = manager.Get(LifetimeContainer);
|
||||
if (LifetimeManager.NoValue != value) return value;
|
||||
|
||||
return pipeline(ref context);
|
||||
}
|
||||
};
|
||||
}
|
||||
}
|
||||
}
|
|
@ -4,7 +4,6 @@ using System.Diagnostics;
|
|||
using System.Linq;
|
||||
using System.Reflection;
|
||||
using System.Threading;
|
||||
using System.Threading.Tasks;
|
||||
using Unity.Builder;
|
||||
using Unity.Events;
|
||||
using Unity.Extension;
|
|
@ -0,0 +1,15 @@
|
|||
using System;
|
||||
using System.Collections;
|
||||
using System.Collections.Generic;
|
||||
using System.Diagnostics;
|
||||
using System.Linq;
|
||||
using System.Reflection;
|
||||
using Unity.Builder;
|
||||
using Unity.Resolution;
|
||||
|
||||
namespace Unity
|
||||
{
|
||||
public partial class UnityContainer
|
||||
{
|
||||
}
|
||||
}
|
|
@ -0,0 +1,161 @@
|
|||
using System;
|
||||
using System.Collections;
|
||||
using System.Collections.Generic;
|
||||
using System.Diagnostics;
|
||||
using System.Linq;
|
||||
using System.Reflection;
|
||||
using Unity.Builder;
|
||||
using Unity.Resolution;
|
||||
using Unity.Storage;
|
||||
using Unity.Utility;
|
||||
|
||||
namespace Unity
|
||||
{
|
||||
public partial class UnityContainer
|
||||
{
|
||||
#region Fields
|
||||
|
||||
private static readonly MethodInfo EnumerableMethod =
|
||||
typeof(UnityContainer).GetTypeInfo()
|
||||
.GetDeclaredMethod(nameof(UnityContainer.EnumerableHandler));
|
||||
|
||||
private static readonly MethodInfo EnumerableFactory =
|
||||
typeof(UnityContainer).GetTypeInfo()
|
||||
.GetDeclaredMethod(nameof(UnityContainer.ResolverFactory));
|
||||
|
||||
#endregion
|
||||
|
||||
|
||||
#region TypeResolverFactory
|
||||
|
||||
public static TypeFactoryDelegate EnumerableTypeFactory = (Type type, UnityContainer container) =>
|
||||
{
|
||||
#if NETSTANDARD1_0 || NETCOREAPP1_0 || NET40
|
||||
var typeArgument = type.GetTypeInfo().GenericTypeArguments.First();
|
||||
if (typeArgument.GetTypeInfo().IsGenericType)
|
||||
#else
|
||||
Debug.Assert(0 < type.GenericTypeArguments.Length);
|
||||
var typeArgument = type.GenericTypeArguments.First();
|
||||
if (typeArgument.IsGenericType)
|
||||
#endif
|
||||
{
|
||||
return ((EnumerableFactoryDelegate)
|
||||
EnumerableFactory.MakeGenericMethod(typeArgument)
|
||||
.CreateDelegate(typeof(EnumerableFactoryDelegate)))();
|
||||
}
|
||||
else
|
||||
{
|
||||
return (ResolveDelegate<BuilderContext>)
|
||||
EnumerableMethod.MakeGenericMethod(typeArgument)
|
||||
.CreateDelegate(typeof(ResolveDelegate<BuilderContext>));
|
||||
}
|
||||
};
|
||||
|
||||
#endregion
|
||||
|
||||
|
||||
#region Implementation
|
||||
|
||||
private static object EnumerableHandler<TElement>(ref BuilderContext context)
|
||||
{
|
||||
return ((UnityContainer)context.Container).ResolveEnumerable<TElement>(context.Resolve,
|
||||
context.Name);
|
||||
}
|
||||
|
||||
private static ResolveDelegate<BuilderContext> ResolverFactory<TElement>()
|
||||
{
|
||||
Type type = typeof(TElement).GetGenericTypeDefinition();
|
||||
return (ref BuilderContext c) => ((UnityContainer)c.Container).ResolveEnumerable<TElement>(c.Resolve, type, c.Name);
|
||||
}
|
||||
|
||||
#endregion
|
||||
|
||||
|
||||
#region Nested Types
|
||||
|
||||
private delegate ResolveDelegate<BuilderContext> EnumerableFactoryDelegate();
|
||||
|
||||
#endregion
|
||||
|
||||
|
||||
#region Enumerator
|
||||
|
||||
private class EnumerableEnumerator<TType> : IEnumerator<TType>
|
||||
{
|
||||
#region Fields
|
||||
|
||||
private int _prime = 5;
|
||||
private int[] Buckets;
|
||||
private Entry[] Entries;
|
||||
UnityContainer _container;
|
||||
private int Count;
|
||||
|
||||
#endregion
|
||||
|
||||
|
||||
#region Constructors
|
||||
|
||||
public EnumerableEnumerator(UnityContainer container)
|
||||
{
|
||||
_container = container;
|
||||
var size = HashHelpers.Primes[_prime];
|
||||
Buckets = new int[size];
|
||||
Entries = new Entry[size];
|
||||
|
||||
#if !NET40
|
||||
unsafe
|
||||
{
|
||||
fixed (int* bucketsPtr = Buckets)
|
||||
{
|
||||
int* ptr = bucketsPtr;
|
||||
var end = bucketsPtr + Buckets.Length;
|
||||
while (ptr < end) *ptr++ = -1;
|
||||
}
|
||||
}
|
||||
#else
|
||||
for(int i = 0; i < Buckets.Length; i++)
|
||||
Buckets[i] = -1;
|
||||
#endif
|
||||
}
|
||||
|
||||
#endregion
|
||||
|
||||
|
||||
#region IEnumerator
|
||||
|
||||
public TType Current => throw new NotImplementedException();
|
||||
|
||||
object IEnumerator.Current => throw new NotImplementedException();
|
||||
|
||||
public void Dispose()
|
||||
{
|
||||
throw new NotImplementedException();
|
||||
}
|
||||
|
||||
public bool MoveNext()
|
||||
{
|
||||
throw new NotImplementedException();
|
||||
}
|
||||
|
||||
public void Reset()
|
||||
{
|
||||
throw new NotImplementedException();
|
||||
}
|
||||
|
||||
#endregion
|
||||
}
|
||||
|
||||
#endregion
|
||||
|
||||
|
||||
#region Entry Type
|
||||
|
||||
private struct Entry
|
||||
{
|
||||
public HashKey Key;
|
||||
public int Next;
|
||||
}
|
||||
|
||||
#endregion
|
||||
}
|
||||
}
|
|
@ -263,7 +263,7 @@ namespace Unity
|
|||
return argType;
|
||||
}
|
||||
|
||||
internal IEnumerable<TElement> ResolveArray<TElement>(Func<Type, IRegistration, object?> resolve, Type type)
|
||||
internal IEnumerable<TElement> ResolveArray<TElement>(Func<Type, string?, object?> resolve, Type type)
|
||||
{
|
||||
object? value;
|
||||
var set = new QuickSet();
|
||||
|
@ -292,7 +292,7 @@ namespace Unity
|
|||
{
|
||||
try
|
||||
{
|
||||
value = resolve(typeof(TElement), registration);
|
||||
value = resolve(typeof(TElement), registration.Name);
|
||||
}
|
||||
catch (ArgumentException ex) when (ex.InnerException is TypeLoadException)
|
||||
{
|
||||
|
@ -308,7 +308,7 @@ namespace Unity
|
|||
}
|
||||
}
|
||||
|
||||
internal IEnumerable<TElement> ResolveArray<TElement>(Func<Type, IRegistration, object?> resolve,
|
||||
internal IEnumerable<TElement> ResolveArray<TElement>(Func<Type, string?, object?> resolve,
|
||||
Type type, Type typeDefinition)
|
||||
{
|
||||
object? value;
|
||||
|
@ -340,7 +340,7 @@ namespace Unity
|
|||
{
|
||||
try
|
||||
{
|
||||
value = resolve(typeof(TElement), registration);
|
||||
value = resolve(typeof(TElement), registration.Name);
|
||||
}
|
||||
catch (ArgumentException ex) when (ex.InnerException is TypeLoadException)
|
||||
{
|
||||
|
@ -367,9 +367,7 @@ namespace Unity
|
|||
{
|
||||
try
|
||||
{
|
||||
var itemKey = new HashKey(typeof(TElement), registration.Name);
|
||||
var item = container.GetOrAdd(ref itemKey, typeof(TElement), registration.Name, registration);
|
||||
value = resolve(typeof(TElement), item);
|
||||
value = resolve(typeof(TElement), registration.Name);
|
||||
}
|
||||
catch (MakeGenericTypeFailedException) { continue; }
|
||||
catch (InvalidRegistrationException) { continue; }
|
||||
|
@ -383,7 +381,7 @@ namespace Unity
|
|||
}
|
||||
}
|
||||
|
||||
internal IEnumerable<TElement> ComplexArray<TElement>(Func<Type, IRegistration, object?> resolve, Type type)
|
||||
internal IEnumerable<TElement> ComplexArray<TElement>(Func<Type, string?, object?> resolve, Type type)
|
||||
{
|
||||
object? value;
|
||||
var set = new QuickSet();
|
||||
|
@ -413,9 +411,7 @@ namespace Unity
|
|||
{
|
||||
try
|
||||
{
|
||||
var itemKey = new HashKey(typeof(TElement), registration.Name);
|
||||
var item = container.GetOrAdd(ref itemKey, typeof(TElement), registration.Name, registration);
|
||||
value = resolve(typeof(TElement), item);
|
||||
value = resolve(typeof(TElement), registration.Name);
|
||||
}
|
||||
catch (ArgumentException ex) when (ex.InnerException is TypeLoadException)
|
||||
{
|
||||
|
@ -431,7 +427,7 @@ namespace Unity
|
|||
}
|
||||
}
|
||||
|
||||
internal IEnumerable<TElement> ComplexArray<TElement>(Func<Type, IRegistration, object?> resolve,
|
||||
internal IEnumerable<TElement> ComplexArray<TElement>(Func<Type, string?, object?> resolve,
|
||||
Type type, Type typeDefinition)
|
||||
{
|
||||
object? value;
|
||||
|
@ -462,9 +458,7 @@ namespace Unity
|
|||
{
|
||||
try
|
||||
{
|
||||
var itemKey = new HashKey(typeof(TElement), registration.Name);
|
||||
var item = container.GetOrAdd(ref itemKey, typeof(TElement), registration.Name);
|
||||
value = resolve(typeof(TElement), item);
|
||||
value = resolve(typeof(TElement), registration.Name);
|
||||
}
|
||||
catch (ArgumentException ex) when (ex.InnerException is TypeLoadException)
|
||||
{
|
||||
|
@ -491,10 +485,8 @@ namespace Unity
|
|||
{
|
||||
try
|
||||
{
|
||||
var itemKey = new HashKey(typeof(TElement), registration.Name);
|
||||
var item = container.GetOrAdd(ref itemKey, typeof(TElement), registration.Name);
|
||||
#pragma warning disable CS8601 // Possible null reference assignment.
|
||||
value = (TElement)resolve(typeof(TElement), item);
|
||||
value = (TElement)resolve(typeof(TElement), registration.Name);
|
||||
#pragma warning restore CS8601 // Possible null reference assignment.
|
||||
}
|
||||
catch (MakeGenericTypeFailedException) { continue; }
|
Загрузка…
Ссылка в новой задаче