-
Notifications
You must be signed in to change notification settings - Fork 851
Adding support for constrained open generics #536
New issue
Have a question about this project? Sign up for a free GitHub account to open an issue and contact its maintainers and the community.
By clicking “Sign up for GitHub”, you agree to our terms of service and privacy statement. We’ll occasionally send you account related emails.
Already on GitHub? Sign in to your account
Changes from 9 commits
3431716
1286891
3ba35c9
2d241e2
d7a3b82
bb1d6a8
f32e5b8
64e6808
a0b1e93
88b6c1d
70b4847
7246fd4
584de56
8cde246
9f866e0
7499ba2
0626cdc
f99b9da
ae792ae
35c1f89
6e73daa
66a64ba
cd1b606
e0553cb
90bfb82
8fae420
File filter
Filter by extension
Conversations
Jump to
Diff view
Diff view
There are no files selected for viewing
| Original file line number | Diff line number | Diff line change |
|---|---|---|
| @@ -0,0 +1,14 @@ | ||
| // Copyright (c) .NET Foundation. All rights reserved. | ||
| // Licensed under the Apache License, Version 2.0. See License.txt in the project root for license information. | ||
| namespace Microsoft.Extensions.DependencyInjection.Specification.Fakes | ||
| { | ||
| public class ConstrainedFakeOpenGenericService<TVal> : IFakeOpenGenericService<TVal> | ||
| where TVal : PocoClass | ||
| { | ||
| public ConstrainedFakeOpenGenericService(TVal value) | ||
| { | ||
| Value = value; | ||
| } | ||
| public TVal Value { get; } | ||
| } | ||
| } |
| Original file line number | Diff line number | Diff line change |
|---|---|---|
|
|
@@ -7,8 +7,6 @@ | |
| using System.Diagnostics; | ||
| using System.Linq; | ||
| using System.Reflection; | ||
| using System.Runtime.CompilerServices; | ||
| using System.Threading.Tasks; | ||
| using Microsoft.Extensions.Internal; | ||
|
|
||
| namespace Microsoft.Extensions.DependencyInjection.ServiceLookup | ||
|
|
@@ -110,8 +108,7 @@ private ServiceCallSite TryCreateExact(Type serviceType, CallSiteChain callSiteC | |
|
|
||
| private ServiceCallSite TryCreateOpenGeneric(Type serviceType, CallSiteChain callSiteChain) | ||
| { | ||
| if (serviceType.IsConstructedGenericType | ||
| && _descriptorLookup.TryGetValue(serviceType.GetGenericTypeDefinition(), out var descriptor)) | ||
| if (serviceType.IsConstructedGenericType && _descriptorLookup.TryGetValue(serviceType.GetGenericTypeDefinition(), out var descriptor)) | ||
| { | ||
| return TryCreateOpenGeneric(descriptor.Last, serviceType, callSiteChain, DefaultSlot); | ||
| } | ||
|
|
@@ -211,7 +208,8 @@ private ServiceCallSite TryCreateExact(ServiceDescriptor descriptor, Type servic | |
| private ServiceCallSite TryCreateOpenGeneric(ServiceDescriptor descriptor, Type serviceType, CallSiteChain callSiteChain, int slot) | ||
| { | ||
| if (serviceType.IsConstructedGenericType && | ||
| serviceType.GetGenericTypeDefinition() == descriptor.ServiceType) | ||
| serviceType.GetGenericTypeDefinition() == descriptor.ServiceType && | ||
| IsCompatibleWithGenericParameterConstraints(descriptor.ImplementationType, serviceType.GenericTypeArguments)) | ||
| { | ||
| Debug.Assert(descriptor.ImplementationType != null, "descriptor.ImplementationType != null"); | ||
| var lifetime = new ResultCache(descriptor.Lifetime, serviceType, slot); | ||
|
|
@@ -358,6 +356,132 @@ private ServiceCallSite[] CreateArgumentCallSites( | |
| return parameterCallSites; | ||
| } | ||
|
|
||
| private static bool IsCompatibleWithGenericParameterConstraints(Type genericTypeDefinition, Type[] parameters) | ||
| { | ||
| var genericArgumentDefinitions = genericTypeDefinition.GetTypeInfo().GenericTypeParameters; | ||
| for (var i = 0; i < genericArgumentDefinitions.Length; i++) | ||
| { | ||
| var argumentDefinitionTypeInfo = genericArgumentDefinitions[i].GetTypeInfo(); | ||
| var parameter = parameters[i]; | ||
|
||
| var parameterTypeInfo = parameter.GetTypeInfo(); | ||
| foreach (var constraint in argumentDefinitionTypeInfo.GetGenericParameterConstraints()) | ||
| { | ||
| var substituteGenericParameterConstraint = SubstituteGenericParameterConstraint(parameters, constraint); | ||
| if (!ParameterCompatibleWithTypeConstraint(parameter, substituteGenericParameterConstraint)) | ||
| { | ||
| return false; | ||
| } | ||
| } | ||
|
|
||
| var specialConstraints = argumentDefinitionTypeInfo.GenericParameterAttributes; | ||
| if ((specialConstraints & GenericParameterAttributes.DefaultConstructorConstraint) == GenericParameterAttributes.DefaultConstructorConstraint) | ||
| { | ||
| var hasPublicNoArgCtor = false; | ||
| foreach (var c in parameterTypeInfo.DeclaredConstructors) | ||
| { | ||
| if (c.IsPublic) | ||
jbogard marked this conversation as resolved.
Outdated
Show resolved
Hide resolved
|
||
| { | ||
| if (c.GetParameters().Length == 0) | ||
| { | ||
| hasPublicNoArgCtor = true; | ||
| break; | ||
| } | ||
| } | ||
| } | ||
|
|
||
| if (!parameterTypeInfo.IsValueType && !hasPublicNoArgCtor) | ||
| { | ||
| return false; | ||
| } | ||
| } | ||
| if ((specialConstraints & GenericParameterAttributes.ReferenceTypeConstraint) == GenericParameterAttributes.ReferenceTypeConstraint) | ||
| { | ||
| if (parameterTypeInfo.IsValueType) | ||
| { | ||
| return false; | ||
| } | ||
| } | ||
| if ((specialConstraints & GenericParameterAttributes.NotNullableValueTypeConstraint) == GenericParameterAttributes.NotNullableValueTypeConstraint) | ||
| { | ||
| if (!parameterTypeInfo.IsValueType || parameterTypeInfo.IsGenericType && IsGenericTypeDefinedBy(parameter, typeof(Nullable<>))) | ||
| { | ||
| return false; | ||
| } | ||
| } | ||
| } | ||
| return true; | ||
| } | ||
|
|
||
| private static bool IsGenericTypeDefinedBy(Type type, Type openGeneric) | ||
| { | ||
| return !type.ContainsGenericParameters && type.IsGenericType && type.GetGenericTypeDefinition() == openGeneric; | ||
| } | ||
|
|
||
| private static Type SubstituteGenericParameterConstraint(Type[] parameters, Type constraint) | ||
| { | ||
| return constraint.IsGenericParameter ? parameters[constraint.GenericParameterPosition] : constraint; | ||
| } | ||
|
|
||
| private static bool ParameterCompatibleWithTypeConstraint(Type parameter, Type constraint) | ||
| { | ||
| if (constraint.IsAssignableFrom(parameter)) | ||
| { | ||
| return true; | ||
| } | ||
|
|
||
| if (ParameterEqualsConstraint(parameter, constraint)) | ||
| { | ||
| return true; | ||
| } | ||
|
|
||
| if (parameter.BaseType != null && ParameterEqualsConstraint(parameter.BaseType, constraint)) | ||
|
||
| { | ||
| return true; | ||
| } | ||
|
|
||
| foreach (var interfaceType in parameter.GetTypeInfo().ImplementedInterfaces) | ||
| { | ||
| if (ParameterEqualsConstraint(interfaceType, constraint)) | ||
| { | ||
| return true; | ||
| } | ||
| } | ||
|
|
||
| return false; | ||
| } | ||
|
|
||
| private static bool ParameterEqualsConstraint(Type parameter, Type constraint) | ||
| { | ||
| var genericArguments = parameter.GenericTypeArguments; | ||
| if (genericArguments.Length > 0 && constraint.IsGenericType) | ||
| { | ||
| var typeDefinition = constraint.GetGenericTypeDefinition(); | ||
| if (typeDefinition.GetTypeInfo().GenericTypeParameters.Length == genericArguments.Length) | ||
| { | ||
| var constraintArguments = constraint.GetTypeInfo().GenericTypeArguments; | ||
| for (var i = 0; i < constraintArguments.Length; i++) | ||
| { | ||
| var constraintArgument = constraintArguments[i]; | ||
| if (!constraintArgument.IsGenericParameter && !constraintArgument.IsAssignableFrom(genericArguments[i])) | ||
pakrym marked this conversation as resolved.
Outdated
Show resolved
Hide resolved
|
||
| { | ||
| return false; | ||
| } | ||
| } | ||
|
|
||
| Type genericType; | ||
| try | ||
| { | ||
| genericType = typeDefinition.MakeGenericType(genericArguments); | ||
jbogard marked this conversation as resolved.
Outdated
Show resolved
Hide resolved
|
||
| } | ||
| catch (ArgumentException) | ||
| { | ||
| return false; | ||
| } | ||
| return genericType == parameter; | ||
| } | ||
| } | ||
| return false; | ||
| } | ||
|
|
||
| public void Add(Type type, ServiceCallSite serviceCallSite) | ||
| { | ||
|
|
||
| Original file line number | Diff line number | Diff line change |
|---|---|---|
| @@ -0,0 +1,20 @@ | ||
| // Copyright (c) .NET Foundation. All rights reserved. | ||
| // Licensed under the Apache License, Version 2.0. See License.txt in the project root for license information. | ||
|
|
||
| namespace Microsoft.Extensions.DependencyInjection.Tests.Fakes | ||
| { | ||
| public class ClassInheritingAbstractClass : AbstractClass | ||
| { | ||
|
|
||
| } | ||
|
|
||
| public class ClassAlsoInheritingAbstractClass : AbstractClass | ||
| { | ||
|
|
||
| } | ||
|
|
||
| public class ClassInheritingClassInheritingAbstractClass : AbstractClass | ||
| { | ||
|
|
||
| } | ||
| } |
| Original file line number | Diff line number | Diff line change |
|---|---|---|
| @@ -0,0 +1,17 @@ | ||
| // Copyright (c) .NET Foundation. All rights reserved. | ||
| // Licensed under the Apache License, Version 2.0. See License.txt in the project root for license information. | ||
|
|
||
| using Microsoft.Extensions.DependencyInjection.Specification.Fakes; | ||
|
|
||
| namespace Microsoft.Extensions.DependencyInjection.Fakes | ||
| { | ||
| public class ConstrainedFakeOpenGenericService<TVal> : IFakeOpenGenericService<TVal> | ||
| where TVal : PocoClass | ||
| { | ||
| public ConstrainedFakeOpenGenericService(TVal value) | ||
| { | ||
| Value = value; | ||
| } | ||
| public TVal Value { get; } | ||
| } | ||
| } |
Uh oh!
There was an error while loading. Please reload this page.