根据Andrew Hare的正确答案更新了问题:
给出以下C#类:
public class Bar : Foo, IDisposable
{
// implementation of Bar and IDisposable
}
public class Foo : IEnumerable<int>
{
// implementation of Foo and all its inherited interfaces
}
我想要一个类似下面的方法,它不会在断言上失败(注意:你不能改变断言):
public void SomeMethod()
{
// This doesn't work
Type[] interfaces = typeof(Bar).GetInterfaces();
Debug.Assert(interfaces != null);
Debug.Assert(interfaces.Length == 1);
Debug.Assert(interfaces[0] == typeof(IDisposable));
}
有人可以通过修复此方法来帮助断言不会失败吗?
调用typeof(Bar).GetInterfaces()
不起作用,因为它返回整个界面层次结构(即interfaces
变量包含IEnumerable<int>
,IEnumerable
和IDisposable
),而不是只是顶级。
答案 0 :(得分:8)
试试这个:
using System.Linq;
public static class Extensions
{
public static Type[] GetTopLevelInterfaces(this Type t)
{
Type[] allInterfaces = t.GetInterfaces();
var selection = allInterfaces
.Where(x => !allInterfaces.Any(y => y.GetInterfaces().Contains(x)))
.Except(t.BaseType.GetInterfaces());
return selection.ToArray();
}
}
用法:
private void Check(Type t, Type i)
{
var interfaces = t.GetTopLevelInterfaces();
Debug.Assert(interfaces != null, "interfaces is null");
Debug.Assert(interfaces.Length == 1, "length is not 1");
Debug.Assert(interfaces[0] == i, "the expected interface was not found");
System.Console.WriteLine("\n{0}", t.ToString());
foreach (var intf in interfaces)
System.Console.WriteLine(" " + intf.ToString());
}
public void Run()
{
Check(typeof(Foo), typeof(IEnumerable<int>));
Check(typeof(Bar), typeof(IDisposable));
}
如其他地方所述,这仅在checked类型显式实现单个接口时才有效。如果您有多个,则需要更改断言。
答案 1 :(得分:2)
由于您从接口层次结构中检索所有接口,因此实际上没有任何方法可以执行此操作。这意味着当您实施IEnumerable<T>
时,您也隐式实施IEnumerable
。
换句话说,如果你看一下你创建的类的IL,你会看到:
.class public auto ansi beforefieldinit Foo
extends [mscorlib]System.Object
implements [mscorlib]System.Collections.Generic.IEnumerable`1<int32>,
[mscorlib]System.Collections.IEnumerable
{
// ...
}
即使您只表明您的类型实现了IEnumerable<T>
,编译器也会发出IL,指示您的类型实现IEnumerable<T>
和IEnumerable
。
反射API很高兴地返回你在类型上实际定义的内容(这是你的类型实现两个接口 - 它实际上是这样做的)。 C#编译器允许您仅引用接口层次结构中最底层的类型,因为它将填充您的类型也实现的其他接口。这是接口继承与类型继承的不同方式之一。
答案 2 :(得分:2)
Andrew Hare是正确的,您无法使用反射检索指定的接口列表。但是,您可以通过排除其他人隐含的任何接口来找到“顶级”接口。你可以像这样实现它:
Type[] allInterfaces = typeof(Foo).GetInterfaces();
Type[] interfaces = allInterfaces
.Where(x => !allInterfaces.Any(y => y.GetInterfaces().Contains(x)))
.ToArray();
这会传递你的断言。
答案 3 :(得分:2)
你只想获得第一级接口,对吧?你可以混搭一些LINQ和反射;只是排除基类型正在实现的任何内容。
var fooType = typeof(Foo);
if(fooType.BaseType == null)
return fooType.GetInterfaces().ToArray();
return fooType
.GetInterfaces()
.Except(fooType.BaseType.GetInterfaces())
.ToArray();
答案 4 :(得分:0)
我会把它写成:
public void SomeMethod()
{
Type[] interfaces = typeof(Foo).GetInterfaces();
Debug.Assert(interfaces.Contains(typeof(IEnumerable<int>)));
}
但是如果不知道你想要测试什么,就很难回答。无论如何,在使用GetInterfaces
时你应该not rely on the order,如果类型没有实现任何类型,那么该方法将返回一个空数组,因此不需要进行空检查。
编辑:如果你真的无法改变断言,那么安全的做法是:
Type[] allInterfaces = typeof(Foo).GetInterfaces();
var interfaces = allInterfaces.Where(x => x == typeof(IEnumerable<int>)).ToArray();
Debug.Assert(interfaces != null);
Debug.Assert(interfaces.Length == 1);
Debug.Assert(interfaces[0] == typeof(IEnumerable<int>));
答案 5 :(得分:0)
注意:已更新以过滤继承的接口。
您可以排除基本接口成员,如下所示:
public Type[] GetDeclaredInterfaces( Type type )
{
if( type == typeof(object) )
return new Type[ 0 ];
Type[] interfaces = type.GetInterfaces();
Type[] baseInterfaces = interfaces.Where( i => i.BaseType != null && i.BaseType.IsInterface );
Type[] declaredInterfaces = interfaces.Except( type.BaseType.GetInterfaces() );
return declaredInterfaces.Except( baseInterfaces );
}