2010-03-25 36 views
8

更新問題給出安德魯野兔的正確答案:如何列舉在繼承類上直接定義的接口列表?

下面的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>IEnumerableIDisposable),而不僅僅是頂層。

+0

爲什麼你會這樣做?如果斷言Bar實現了IDisposable,會不會更好更乾淨呢? – Svish 2010-03-25 21:09:54

+0

@Svish - 這是一個人爲的例子。我正在開發一個自定義的IoC自動綁定工具,它需要查找最頂級的接口。我想我會簡化場景來回答我之前的根本問題,而不是把它與IoC噪音混淆起來。另外,現在當別人需要在不同背景下回答這個問題時,IoC噪音不會妨礙他們。 – Jordan 2010-03-26 13:43:59

回答

7

試試這個:

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)); 
    } 

正如其他地方所指出,這隻能如果檢查到類型顯式實現一個接口。如果你有多個,那麼你需要改變你的斷言。

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#編譯器允許您僅引用接口層次結構中最底層的類型,因爲它將填充類型也實現的其他接口。這是接口繼承與類型繼承不同的方式之一。

+0

安德魯 - 你的回答是正確的;我的問題並不完整。謝謝! – Jordan 2010-03-25 20:07:00

+0

其實你可以通過查看接口類型的BaseType來過濾這些。在別處看到我的回答。 – 2010-03-25 20:10:02

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>)); 
+0

如何在不更改斷言的情況下編寫它? – Jordan 2010-03-25 19:53:18

+2

我不會。第一個斷言(空檢查)將始終通過(儘管它不是有害的),如果僅在您的控制下實現接口,並且第三個(通過索引訪問)在文檔中被特別警告,則第二個斷言僅具有含義。 – 2010-03-25 20:00:47

+0

是真實的,但他專門將這些斷言僅用於他原來的Foo類型,它明確實現了一個單一的接口。你的「安全代碼」是一個重言式。它有點回避了整個問題。 – Cheeso 2010-03-25 20:19:01

2

安德魯·黑爾是正確的,你不能檢索指定的使用反射接口列表。但是,通過排除其他人隱含的任何接口,您可以找到「頂級」接口。你可以這樣實現:

Type[] allInterfaces = typeof(Foo).GetInterfaces(); 
Type[] interfaces = allInterfaces 
    .Where(x => !allInterfaces.Any(y => y.GetInterfaces().Contains(x))) 
    .ToArray(); 

這通過你的斷言。

+0

好了......直到他改變了他的問題!#%!!%! – 2010-03-25 20:13:50

+1

+1,因爲它*是*正確的一點;-) – 2010-03-25 20:18:24

+0

+1以及 - 謝謝你的答案! – Jordan 2010-03-25 20:21:31

2

你只想得到一級接口,對不對?你可以混合一些LINQ和反射;只是排除基類型正在實現的任何東西。

var fooType = typeof(Foo); 

if(fooType.BaseType == null) 
    return fooType.GetInterfaces().ToArray(); 

return fooType 
    .GetInterfaces() 
    .Except(fooType.BaseType.GetInterfaces()) 
    .ToArray(); 
+0

哈。 Mark說什麼。雖然現在我看到安德魯的回答了,但這並不是你要問的,但它確實通過了你的斷言。 – 2010-03-25 19:55:29

+0

這適用於這種情況,但如果該類實現多個接口,可能會失敗,因爲GetInterface不會以一致的順序返回它們。 – 2010-03-25 20:15:29

+0

@Jamie,這是真的,我會說這個問題構成不好。他沒有推論出頂層類型實現多個接口的情況。 – Cheeso 2010-03-25 20:45:35

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); 
}