2012-06-25 87 views
3

我比較泛型類型的is運算符有問題。C#:是具有繼承性的泛型類型的運算符

public interface ISomeInterface<T> where T : SomeBaseClass{ 
} 

public class SomeClass : SomeBaseClass{ 
} 

現在我們要用is運算符來檢查類型。我們有一個實現接口ISomeInterface的類的實例。

不幸的是,我們正面臨着以下問題:

// someObject is an Instance of a class implementing interface ISomeInterface<SomeClass> 
bool isSomeBaseClass = someObject is ISomeInterface<SomeBaseClass>; // false 
bool isSomeClass = someObject is ISomeInterface<SomeClass>; // true 

是否有可能檢查變量泛型類型?

由於提前, 託比

+0

請清理您的C&P - 您的兩個測試都是相同的。一個'someObject'的聲明也會很好。 –

+2

運算符返回「false」的原因是因爲「ISomeInterface 」不是** ISomeInterface 的子類型。很容易想象一個例子,第一個到第二個會導致類型安全錯誤。 (例如,將一個'IList '投射到'IList ',然後給它添加一個整數。) – millimoose

+0

@millimoose這正是問題所在。但是我怎樣才能檢查它,如果我想成爲一個IList 的對象是真實的IList ? – Tobias

回答

8

這就是所謂的通用協方差和支持在C#4.0。你可以用關鍵字out標誌着通用T參數:

public interface ISomeInterface<out T> where T : SomeBaseClass 

這有一個限制,但。 T參數只能顯示爲接口中方法的返回類型。

Eric Lippert在這個問題上有一個series of blog posts,我邀請您閱讀。

4

是的,你可以使用inout關鍵字利用協方差和逆變:

public interface ISomeInterface<in T> where T : SomeBaseClass{ 

} 

或者:

public interface ISomeInterface<out T> where T : SomeBaseClass{ 

} 

但請記住,使用關鍵字in可以使用牛逼作爲參數,否則使用out您可以使用T作爲返回類型。

協方差:

A型是協變的時候,你可以轉換從X<S>X<B>

逆變:

A型逆變時,你可以從轉換到X<B>X<S>

- 其中S是子類,B是基類。


一個有趣的例子是我學習的時候我讀的C#4.0我的書是關於堆棧。

class Stack<T>{ 
    int i; 
    T[] array = new T[1000]; 
    public void Push(T element){ 
     array[i++] = element; 
    } 
} 

class BaseClass{ 
} 

class SubClass : BaseClass{ 
} 

逸岸它解釋說,逆變能夠在這種情況下使用,當堆棧實現此接口:

interface IPushable<in T>{ 
    void Push(T element); 
} 

然後:

IPushable<BaseClass> stackB = new Stack<BaseClass>(); 
IPushable<SubClass> stackS = stackB; 
stackS.Push(new SubClass()); 

當協方差INT這種情況下,當堆棧實現以下接口:

interface IPoppable<in T>{ 
    T Pop(); 
} 

所以後來:

IPoppable<SubClass> stackS = new Stack<SubClass>(); 
IPoppable<BaseClass> stackB = stackB; 
BaseClass baseClass = stackB.Pop(); 

這是真正有用的,因爲它允許upcasts和向下轉換沒有任何問題,編譯時錯誤。

1

不知道我理解正確你的問題,但你可能需要像回答Check if a class is derived from a generic class

public static bool IsSubclassOfRawGeneric(Type generic, Type toCheck) 
{ 
    while (toCheck != null && toCheck != typeof(object)) 
    { 
     var cur = toCheck.IsGenericType ? toCheck.GetGenericTypeDefinition() : toCheck; 
     if (generic == cur) 
      return true; 
     toCheck = toCheck.BaseType; 
    } 
    return false; 
} 
0

答案與你的第二個問題開始,因爲它似乎表明你想達到什麼目的:

問:但是我怎樣才能檢查它,如果我想得到一個IList<string>的對象是真的是IList<object>

你的推理似乎是因爲字符串從對象繼承,你想要一個條件模式來檢查通用情況下。

IList<string> stringList = new List<string>(); 
IList<object> objectList = new List<object>(); 


stringList is IList<string>; 
//> true 
stringList is IList<object>; 
//> false 
objectList is IList<string>; 
//> false 
objectList is IList<object>; 
//>true 
"someString" is object 
//> true 

因此,你只需選中類型的通用型結構的:

與前述的分別IsGenericTypeIsGenericConstructedType,布爾檢查一起。

objectList.GetType().GenericTypeArguments[0] is object 
//> true 
stringList.GetType().GenericTypeArguments[0] is object 
//> true 

警告:檢查空案件;使用空合併和/或空的條件,如果運營商支持的語言,明智的,等等......這不是在例如,對於簡潔和清晰


問:是否可以檢查變量(繼承)通用類型?

除了由達林和fuex答案,進一步指針:

  • 可以使用IEquality實施Type嚴格類型檢查:

    bool condition = (someObject != null 
            && someObject.GetType().Equals(typeof(ISomeInterface<SomeClass>))); 
    
  • 你檢查明確地檢查用於接口:

    var interfaces = someType.GetType().GetInterfaces(); 
    //DotNet4.5: var interfaces = someType.GetType() 
    //    .GetTypeInfo().ImplementedInterfaces; 
    bool condition = (interfaces != null && interfaces.ToList() 
           .Contains(typeof(ISomeInterface<SomeClass>)) == true); 
    

您幾乎可以使用Type和TypeInfo構造任何條件檢查