C#中的反射是否提供了一种方法来确定某些给定System.Type类型的模型是否具有某些接口?
1 2 3 4 5 6
| public interface IMyInterface {}
public class MyType : IMyInterface {}
// should yield 'true'
typeof(MyType )./* ????? */MODELS_INTERFACE (IMyInterface ); |
你在我头脑中有几个选择
typeof(IMyInterface).IsAssignableFrom(typeof(MyType))
typeof(MyType).GetInterfaces().Contains(typeof(IMyInterface))
对于通用接口,它有点不同。
1
| typeof(MyType ).GetInterfaces().Any(i => i .IsGenericType && i .GetGenericTypeDefinition() == typeof(IMyInterface <>)) |
- 记住typeof(imyinterface).isassignablefrom(typeof(imyinterface))也是正确的,这可能会在代码中产生意外的结果。
- 很容易不引起注意,把IsAssignableFrom的论点倒过来。我现在就和GetInterfaces一起去:p
- 在我的代码中,IsAssignableFrom(t1)变体比GetInterfaces().Contains(t2)变体快3倍。
- 也可以使用typeof(MyType).GetInterface("IMyInterface") != null。
- @Pierrearnaud:IsassignableFrom最终会调用GetInterfaces,因此您的测试可能首先检查了GetInterfaces,然后检查了Isassignable。这是因为getInterfaces缓存它的结果,所以第一次调用的成本更高
- 我对IsAssignableFrom的性能测试是通过用相同的参数反复调用它来完成的。所以,是的,通过这种方法进行的任何缓存都会产生显著的加速。
- 在写这篇评论时,你的电话是404,这里是A+1,这样你就可以再次被发现了。
- 对@kosta答案的小小改变。有了C 6,我们就可以进行typeof(MyType).GetInterface(nameof(IMyInterface)) != null,以提高类型安全性和重构性。
- 仍然存在使用相同名称的多个接口的风险。
- 做了一些测试,发现IsAssignableFrom比GetInterfaces().Contains快6倍。另一方面,GetInterfaces是缓存的,但IsAssignableFrom不是缓存的。即使使用高速缓存,GetInterfaces().Contains也比IsAssignableFrom慢3.5倍。
- 另一个注意事项是,只有当接口由类型"直接"实现时,isassignablefrom才返回true:即interface A{} interface B:A{} class C:B{}typeof(C).IsAssignableFrom(typeof(A))返回false,而typeof(C).GetInterfaces().Contains(typeof(A))或typeof(C).GetInterface(nameof(A)) != null都是true。
使用Type.IsAssignableFrom:
1
| typeof(IMyInterface ).IsAssignableFrom(someclass .GetType()); |
或
- 如果您已经有了类的实例,那么更好的方法只是someclass is IMyInterface,因为这根本不涉及反射的成本。所以,虽然不是错的,但这不是一个理想的方法。
- @詹姆斯-同意。即使是雷瑟也给出了同样的建议。
- @贾梅斯J.Reganiv你应该把这作为回答,我几乎错过了你的评论。
- @谢谢,但评论没有回答最初的问题。这个问题要求反射解决方案,我只是说在这个答案的第一个案例中,你确实有一个对象反射的实例,这不是理想的解决方案。
- @Jamesj.Reganiv实际上,is在继承层次的两个方向上都进行检查,而IsAssignableFrom只向上检查。另外,如果您有一个对象实例,您应该调用IsInstanceOfType(它也只向上看)。
- @Universe is只检查一个方向,否则new object() is string是正确的。
- @乔汉娜那不是我的意思,但我觉得我很困惑。
1 2 3 4 5 6 7 8 9
| public static bool ImplementsInterface( this Type type, Type ifaceType ) {
Type[] intf = type.GetInterfaces();
for ( int i = 0; i < intf.Length; i++ ) {
if ( intf[ i ] == ifaceType ) {
return true;
}
}
return false;
} |
我认为这是正确的版本,有三个原因:
1)它使用getInterfaces而不是isassignableFrom,因为isassignableFrom最终在多次检查后调用getInterfaces,所以速度更快。2)它在本地数组上迭代,因此没有边界检查。3)它使用为类型定义的==运算符,因此可能比equals方法(包含调用,最终将使用)更安全。
- +1至于内容,我讨厌帕伦斯和埃及大括号周围的空格。整个方法也可以写成:return-type.getInterfaces().any(t=>t==ifacetype);
- type.isassignablefrom()internaly的行为与代码完全相同
- 另外,为什么不键入.getInterfaces()。包含不使用Linq的(ifacetype)。
我只是这样做的:
1 2 3 4
| public static bool Implements (this Type source ) where I : class
{
return typeof(I ).IsAssignableFrom(source );
} |
我希望我可以说where I : interface,但interface不是通用参数约束选项。class是最接近的。
用途:
1 2
| if(MyType.Implements<IInitializable>())
MyCollection.Initialize(); |
我刚才说的Implements,因为这更直观。我总是让IsAssignableFrom触发器。
- 您可以执行return typeof(I).IsInterface && typeof(I).IsAssignableFrom(source);以对方法的任何"不正确"用法返回false,也就是说,将它与类类型而不是接口类型一起使用,或者如果类型参数不是接口,则引发异常。尽管您可能认为派生类"实现"它是父类…
修改Jeff的答案以获得最佳性能(得益于Pierre Arnaud的性能测试):
1 2
| var type = typeof(MyType );
var implementsInterface = typeof(IMyInterface ).IsAssignableFrom(type ) && type .IsClass; |
要查找在给定的Assembly中实现接口的所有类型:
1 2
| var implementations = typeof(TypeInTargetAssembly ).Assembly.GetTypes()
.Where(t => typeof(IMyInterface ).IsAssignableFrom(t ) && t .IsClass); |
正如其他人已经提到的:本杰明4月10日13时22:21"
It sure was easy to not pay attention and get the arguments for
IsAssignableFrom backwards. I will go with GetInterfaces now :p –
好吧,另一种方法只是创建一个简短的扩展方法,在某种程度上实现了"最常见"的思维方式(并且同意这是一个很小的个人选择,根据个人喜好使它稍微"更自然"):
1 2 3 4 5 6 7
| public static class TypeExtensions
{
public static bool IsAssignableTo(this Type type, Type assignableType)
{
return assignableType.IsAssignableFrom(type);
}
} |
为什么不更通用一点(不确定它是否真的那么有趣,我假设我只是传递了另一点"合成"糖):
1 2 3 4 5 6 7 8 9 10 11 12
| public static class TypeExtensions
{
public static bool IsAssignableTo (this Type type, Type assignableType )
{
return assignableType .IsAssignableFrom(type );
}
public static bool IsAssignableTo <TAssignable >(this Type type )
{
return IsAssignableTo (type, typeof(TAssignable ));
}
} |
我认为这样做可能更自然,但又一次只是个人意见的问题:
1
| var isTrue = michelleType.IsAssignableTo<IMaBelle>(); |
- 您是否有理由不将实现直接放在扩展方法中?我的意思是,这让你可以双向调用它,但是为什么你需要这样做呢?
- @Marqueiv很抱歉晚了2年才给你回电话,我想当时是个老习惯,为了避免重复代码,用扩展方法包装helper方法,会编辑我的答案:)
- @Marqueiv Done Plus改变了我不使用别名的另一个坏习惯,即Boolean=>bool(我不知道为什么我年轻时有一些严格的"花哨"编码规则)。
IsAssignableFrom现在移到TypeInfo了:
1
| typeof(ISMSRequest ).GetTypeInfo().IsAssignableFrom(typeof(T ).GetTypeInfo()); |
正确答案是
然而,
可能返回错误的结果,如下代码显示字符串和IConvertible:
1 2 3 4 5 6 7 8 9 10 11 12 13
| static void TestIConvertible ()
{
string test ="test";
Type stringType = typeof(string); // or test.GetType();
bool isConvertibleDirect = test is IConvertible ;
bool isConvertibleTypeAssignable = stringType .IsAssignableFrom(typeof(IConvertible ));
bool isConvertibleHasInterface = stringType .GetInterface(nameof(IConvertible )) != null;
Console .WriteLine($ "isConvertibleDirect: {isConvertibleDirect}");
Console .WriteLine($ "isConvertibleTypeAssignable: {isConvertibleTypeAssignable}");
Console .WriteLine($ "isConvertibleHasInterface: {isConvertibleHasInterface}");
} |
结果:
1 2 3
| isConvertibleDirect: True
isConvertibleTypeAssignable: False
isConvertibleHasInterface: True |
- 正如您在接受的答案中看到的,您在使用IsAssignableFrom时交换了类型。就像本杰明和埃霍恩警告的那样。
注意,如果您有一个通用接口IMyInterface,那么它将始终返回false:
1
| typeof(IMyInterface <>).IsAssignableFrom(typeof(MyType )) /* ALWAYS FALSE */ |
这也不起作用:
1
| typeof(MyType ).GetInterfaces().Contains(typeof(IMyInterface <>)) /* ALWAYS FALSE */ |
但是,如果MyType执行IMyInterface这项工作并返回true:
1
| typeof(IMyInterface <MyType >).IsAssignableFrom(typeof(MyType )) |
但是,您可能在运行时不知道类型参数T。一个有点黑客的解决方案是:
1 2
| typeof(MyType ).GetInterfaces()
.Any(x =>x .Name == typeof(IMyInterface <>).Name) |
Jeff的解决方案不那么简单:
1 2 3
| typeof(MyType ).GetInterfaces()
.Any(i => i .IsGenericType
&& i .GetGenericTypeDefinition() == typeof(IMyInterface <>)); |
以下是适用于任何情况的Type的扩展方法:
1 2 3 4 5 6 7 8 9 10
| public static class TypeExtensions
{
public static bool IsImplementing(this Type type, Type someInterface)
{
return type.GetInterfaces()
.Any(i => i == someInterface
|| i.IsGenericType
&& i.GetGenericTypeDefinition() == someInterface);
}
} |
(请注意,上面使用的LINQ可能比循环慢。)
然后你可以做:
1
| typeof(MyType ).IsImplementing(IMyInterface <>) |
怎么样
1
| typeof(IWhatever ).GetTypeInfo().IsInterface |
怎么样
1
| if(MyType as IMyInterface != null) |
?
- 当我有一个实例时,这是显而易见的。当我有一个类型从反射时不有用