2015-01-05 58 views
2

我有以下規範來幫助說明此問題:如何從具有基類型約束的泛型方法獲取typeof(T)?

class when_getting_type_of_generic_argument_using_subtype_instance 
{ 
    static GenericTypeTester _genericTypeTester; 
    static IPet _dog; 
    static Type _result; 

    Establish context = 
     () => 
     { 
      _genericTypeTester = new GenericTypeTester(); 

      _dog = new Dog(); 
     }; 

    Because of = 
     () => _result = _genericTypeTester.Test(_dog); 

    It should_return_the_subtype = 
     () => _result.ShouldEqual(_dog.GetType()); 
} 

class Dog : IPet 
{ 
} 

interface IPet 
{ 
} 

class GenericTypeTester 
{ 
    public Type Test<T>(T dog) where T : IPet 
    { 
     return typeof (T); 
    } 
} 

上述規格失敗,出現以下消息:

預期:System.RuntimeType:[Pepino.ScenarioRunner.Selenium.Specs。狗] 卻被:System.RuntimeType:[Pepino.ScenarioRunner.Selenium.Specs.IPet]

我需要的結果是Dog類型。有沒有什麼我可以做,而不使用反射?

回答

2

這裏的問題是在運行時與編譯時使用的類型。

因爲你宣佈_dog作爲IPet,傳遞到泛型方法的變量是一個IPet在編譯時,儘管在運行時是一個Dog。因此編譯器使用IPet作爲通用參數,即使運行時的對象是Dog。由於您使用了typeof(T),因此編譯器會爲您提供通用方法的確切類型。

這可以通過將_dog的類型更改爲Dog而不是IPet來看出,這會使編譯器推斷出正確的類型。

這也可以通過顯式鑄造對象dynamic避免:

() => _result = _genericTypeTester.Test(_dog as dynamic); 

這將迫使編譯器推遲了類型推斷,直到運行時,這時它會確定對象的類型Dog 。這通常不是生產代碼的好主意,但是,因爲dynamic類型相當慢。

+0

變化'靜態IPET _dog;''以靜態的狗_dog;',你會看到類型轉換成狗 – Grax

+0

@Grax技術上是真實的,所以我會將其納入我的答案 – David

+0

@Grax是正確的,但在運行時,我沒有那種奢侈。類型必須保持IPet。但是,你的「訣竅」鑄造動態,使規格立即通過。 –

0

的constrait 「下課」 只是添加到您的方法:

class GenericTypeTester 
{ 
    public Type Test<T>(T dog) where T : IPet, class 
    { 
    return typeof (T); 
    } 
} 
+0

這會阻止代碼的其餘部分編譯,因爲對象'_dog'是一個'IPet',因此不會滿足給定的約束條件。它也不能解決問題 – David

相關問題