从Generics <T>转换为特定子类

Gra*_*ton 8 c# generics

我有一个班级

public class MyClass<T> where T : OneType
{
   T MyObj { get; set; }

   public MyCLass(T obj)
   {
   }
}

public class SubClass: MyClass<TwoType>
{
}

// snip for other similar class definition
Run Code Online (Sandbox Code Playgroud)

在哪里,TwoType源自OneType.

现在,我有这个实用方法

public static MyClass<T> Factory<T>(T vd)
 where T : OneType
{
   switch(vd.TypeName)
   {
      case Constant.TwoType
          return new SubClass((TwoType)vd);
     // snip for other type check
   }
}
Run Code Online (Sandbox Code Playgroud)

显然,哪个函数检查类型vd,并创建适当的MyClass类型.唯一的问题是上面的代码不会编译,我不知道为什么

错误是

无法将T的表达式转换为TwoType

Eri*_*ert 18

正如Grzenio正确指出的那样,T类型的表达式不能转换为TwoType.编译器不知道表达式是保证类型为TwoType - 由"if"语句保证,但编译器在分析类型时不考虑if语句的含义.相反,编译器假定T可以是满足约束的任何类型,包括ThreeType,一种从OneType派生但不是TwoType的类型.显然没有从ThreeType到TwoType的转换,因此也没有从T到TwoType的转换.

您可以通过说"好吧,将T视为对象,然后将对象转换为TwoType"来欺骗编译器允许它.沿途的每一步都是合法的 - T可以转换为对象,并且可能存在从对象到TwoType的转换,因此编译器允许它.

然后,您将从SubClass转换为相同的问题MyClass<T>.同样,您可以先通过强制转换为对象来解决问题.

但是,这段代码仍然是错误的:

public static MyClass<T> Factory<T>(T vd) 
 where T:OneType 
{ 
   switch(vd.TypeName) 
   { 
      case Constant.TwoType 
       // WRONG
       return (MyClass<T>)(object)(new SubClass((TwoType)(object)vd)); 
     // snip for other type check 
   } 
} 
Run Code Online (Sandbox Code Playgroud)

为什么这是错的?好吧,考虑一下这里可能出错的一切.例如:你说

class AnotherTwoType : TwoType { }
...
x = Factory<TwoType>(new AnotherTwoType());
Run Code Online (Sandbox Code Playgroud)

怎么了?我们不调用SubClass构造函数,因为参数不完全是TwoType类型,它是从TwoType派生的类型.你可能想要而不是开关

public static MyClass<T> Factory<T>(T vd) 
  where T:OneType 
{ 
  if (vd is TwoType)
       // STILL WRONG
       return (MyClass<T>)(object)(new SubClass((TwoType)(object)vd)); 
  // snip for other type check 
} 
Run Code Online (Sandbox Code Playgroud)

这仍然是错误的.再次,想想可能出现的问题:

x = Factory<OneType>(new TwoType());
Run Code Online (Sandbox Code Playgroud)

现在发生了什么?参数是TwoType,我们创建一个新的SubClass,然后尝试将其转换为MyClass<OneType>,但是没有SubClass的转换,MyClass<OneType>所以这会在运行时崩溃和死亡.

您工厂的正确代码是

public static MyClass<T> Factory<T>(T vd) 
  where T:OneType 
{ 
  if (vd is TwoType && typeof(T) == typeof(TwoType))
       return (MyClass<T>)(object)(new SubClass((TwoType)(object)vd)); 
  // snip for other type check 
} 
Run Code Online (Sandbox Code Playgroud)

现在这一切都清楚了吗?你可以完全考虑一种不同的方法; 当你需要这么多的强制转换和运行时类型检查来说服编译器代码是正确的时,证明整个事情都是一个糟糕的代码味道.


Gra*_*ton 1

令人惊奇的是,我通过编写这样的代码来使其工作:

return (new SubClass(vd as TwoType) as MyClass<T>);
Run Code Online (Sandbox Code Playgroud)

或者

return (MyClass<T>)(object)new SubClass((TwoType)(object)vd );
Run Code Online (Sandbox Code Playgroud)

但,

return (MyClass<T>)new SubClass((TwoType)vd );
Run Code Online (Sandbox Code Playgroud)

不起作用。

as和铸造似乎有区别()