2015-01-05 53 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

相关问题