Determining if an Open Generic Type IsAssignableFrom a Type[source]
xml
<glacius:metadata> | |
<title>Determining if an Open Generic Type IsAssignableFrom a Type</title> | |
<description>How to determine if an open generic type IsAssignableFrom a Type in .NET circa 2011</description> | |
<category>Programming</category> | |
<category>Legacy blog posts</category> | |
<category>C#</category> | |
</glacius:metadata> | |
<glacius:macro name="legacy blargh banner"> | |
<properties> | |
<originalUrl>https://tmont.com/blargh/2011/3/determining-if-an-open-generic-type-isassignablefrom-a-type</originalUrl> | |
<originalDate>2011-03-14T04:04:21.000Z</originalDate> | |
</properties> | |
</glacius:macro> | |
<p>It may come to pass in your dirty .NET dealings that you need to do something like this:</p> | |
<glacius:code lang="csharp"><![CDATA[public void DoTheAwesome(Type type) { | |
if (!typeof(IEnumerable<>).IsAssignableFrom(type)) { | |
return; | |
} | |
// do awesome stuff | |
}]]></glacius:code> | |
<p> | |
If you're asking why you might need to ever do something like this, you clearly haven't | |
worked with enough Inversion of Control frameworks. | |
</p> | |
<p> | |
The point is that the above conditional is always <code>false</code> no matter the value | |
of <code>type</code>. I need it to be <code>true</code> basically if <code>type</code> | |
implements <code>IEnumerable<T></code>, only it wasn't just <code>IEnumerable<T></code> | |
and <code>type</code> could be anything. | |
</p> | |
<p> | |
So a bit of googling and fast-and-furious copypasting from StackOverflow yielded | |
something that worked but not quite (basically the open generic part didn't work, | |
which what I really wanted). But I was able to wrangle it into something that suited | |
my needs. The original code is <a href="https://stackoverflow.com/a/75502">here</a>. | |
</p> | |
<glacius:code lang="csharp"><![CDATA[public static class AssignableExtensions { | |
/// <summary> | |
/// Determines whether the <paramref name="genericType"/> is assignable from | |
/// <paramref name="givenType"/> taking into account generic definitions | |
/// </summary> | |
public static bool IsAssignableToGenericType(this Type givenType, Type genericType) { | |
if (givenType == null || genericType == null) { | |
return false; | |
} | |
return givenType == genericType | |
|| givenType.MapsToGenericTypeDefinition(genericType) | |
|| givenType.HasInterfaceThatMapsToGenericTypeDefinition(genericType) | |
|| givenType.BaseType.IsAssignableToGenericType(genericType); | |
} | |
private static bool HasInterfaceThatMapsToGenericTypeDefinition(this Type givenType, Type genericType) { | |
return givenType | |
.GetInterfaces() | |
.Where(it => it.IsGenericType) | |
.Any(it => it.GetGenericTypeDefinition() == genericType); | |
} | |
private static bool MapsToGenericTypeDefinition(this Type givenType, Type genericType) { | |
return genericType.IsGenericTypeDefinition | |
&& givenType.IsGenericType | |
&& givenType.GetGenericTypeDefinition() == genericType; | |
} | |
}]]></glacius:code> | |
<p> | |
And here are some tests to verify that it might be doing what you think it might | |
or might not be doing: | |
</p> | |
<glacius:code lang="csharp"><![CDATA[[TestFixture] | |
public class AssignableTests { | |
[Test] | |
public void Should_be_assignable_from_open_generic_type_to_concrete_open_generic_type() { | |
Assert.That(typeof(Foo<>).IsAssignableToGenericType(typeof(IFoo<>))); | |
} | |
[Test] | |
public void Should_be_assignable_from_open_generic_type_to_generic_interface_type() { | |
Assert.That(typeof(IFoo<int>).IsAssignableToGenericType(typeof(IFoo<>))); | |
} | |
[Test] | |
public void Should_be_assignable_from_open_generic_type_to_itself() { | |
Assert.That(typeof(IFoo<>).IsAssignableToGenericType(typeof(IFoo<>))); | |
} | |
[Test] | |
public void Should_be_assignable_from_open_generic_type_to_concrete_generic_type() { | |
Assert.That(typeof(Foo<int>).IsAssignableToGenericType(typeof(IFoo<>))); | |
} | |
[Test] | |
public void Should_be_assignable_from_open_generic_type_to_nongeneric_concrete_type() { | |
Assert.That(typeof(Bar).IsAssignableToGenericType(typeof(IFoo<>))); | |
} | |
public interface IFoo<T> {} | |
public class Foo<T> : IFoo<T> { } | |
public class Bar : IFoo<int> { } | |
}]]></glacius:code> | |
<p> | |
This code is licensed under | |
<a href="https://creativecommons.org/licenses/by-sa/3.0/">Creative Commons</a> | |
(same as StackOverflow) so feel free to go to town. | |
</p> | |