Warning: file_get_contents(/data/phpspider/zhask/data//catemap/8/qt/6.json): failed to open stream: No such file or directory in /data/phpspider/zhask/libs/function.php on line 167

Warning: Invalid argument supplied for foreach() in /data/phpspider/zhask/libs/tag.function.php on line 1116

Notice: Undefined index: in /data/phpspider/zhask/libs/function.php on line 180

Warning: array_chunk() expects parameter 1 to be array, null given in /data/phpspider/zhask/libs/function.php on line 181
C# 从泛型转换而来<;T>;到特定子类_C#_Generics - Fatal编程技术网

C# 从泛型转换而来<;T>;到特定子类

C# 从泛型转换而来<;T>;到特定子类,c#,generics,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 显然,哪个函数检查vd的类型,并创建适当的MyClass类型。唯一

我有这样一门课

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
显然,哪个函数检查
vd
的类型,并创建适当的
MyClass
类型。唯一的问题是上面的代码无法编译,我不知道为什么

错误是

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


更改出厂方法:

public static MyClass<T> Factory<T>(T vd)
    where T: OneType
{
    return new MyClass<T>(vd);
}
公共静态MyClass工厂(T vd)
其中T:OneType
{
返回新的MyClass(vd);
}

那么您根本不需要开关。

它在.Net 3.5及以下版本中不起作用-对于任何t,子类都不是
MyClass
类型,它只是
MyClass
类型。泛型类不遵循其模板类型的继承,例如,
MyClass
不是
MyClass
的子类-它们在C#中是完全不同的类


不幸的是,我不知道任何合理的方法来编写工厂方法。

您的实用方法中没有t的约束

public static MyClass<T> Factory<T>(T vd) where T: OneType
{
    // ...
}
公共静态MyClass工厂(T vd),其中T:OneType
{
// ...
}

你能翻转一下设计吗?不要创建需要了解
OneType
的每个子类的僵化工厂方法,而是向
OneType
添加一个抽象方法,如下所示

public MyClass<OneType> GetMyClass();
这应该起作用:

return (MyClass<T>)(object)new SubClass((TwoType)(object)vd);
return(MyClass)(object)新的子类((TwoType)(object)vd);

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

您可以通过说“好吧,将T当作对象,然后将对象强制转换为TwoType”来欺骗编译器允许它。过程中的每一步都是合法的——T可以转换为object,并且可能会有从object到TwoType的转换,因此编译器允许它

然后,将子类转换为
MyClass
时会遇到同样的问题。同样,您可以通过先强制转换到对象来解决问题

但是,该代码仍然是错误的:

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 
   } 
} 
公共静态MyClass工厂(T vd)
其中T:OneType
{ 
开关(vd.TypeName)
{ 
case常量.TwoType
//错
return(MyClass)(object)(新的子类((TwoType)(object)vd));
//其他类型检查用剪子
} 
} 
为什么错了?好吧,想想这里可能出错的一切。例如:你说

class AnotherTwoType : TwoType { }
...
x = Factory<TwoType>(new AnotherTwoType());
class AnotherTwoType:TwoType{}
...
x=工厂(新的另一个TwoType());
会发生什么?我们不调用子类构造函数,因为参数不完全是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 
} 
公共静态MyClass工厂(T vd)
其中T:OneType
{ 
if(vd为两种类型)
//还是错
return(MyClass)(object)(新的子类((TwoType)(object)vd));
//其他类型检查用剪子
} 
这仍然是错误的。再次思考可能出现的问题:

x = Factory<OneType>(new TwoType());
x=工厂(新的TwoType());
现在发生了什么?参数是TwoType,我们创建一个新的子类,然后尝试将其转换为
MyClass
,但没有从子类转换为
MyClass
,因此这将在运行时崩溃和死亡

您工厂的正确代码是

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 
} 
公共静态MyClass工厂(T vd)
其中T:OneType
{ 
如果(vd是TwoType&&typeof(T)=typeof(TwoType))
return(MyClass)(object)(新的子类((TwoType)(object)vd));
//其他类型检查用剪子
} 
现在一切都清楚了吗?你可以完全考虑一种不同的方法;strong>当您需要这么多强制转换和运行时类型检查来说服编译器代码是正确的时,这就证明了整个过程是一种糟糕的代码味道。这对我来说很有效:

public class OneType
{

}

public class MyClass<T> where T : OneType
{
    T MyObj
    { get; set; }
    public MyClass(T obj)
    {
    }
    public static MyClass<T> Factory<T>(T vd)
      where T : OneType
    {
        if (vd is TwoType)
        {
            return (MyClass<T>)(object)new SubClass(vd as TwoType);
        }
        return null;
    }

    public string Working
    {
        get { return this.GetType().Name; }
    }

}

public class TwoType : OneType
{

}


public class SubClass : MyClass<TwoType>
{
    public SubClass(TwoType obj)
        : base(obj)
    {

    }
}
公共类OneType
{
}
公共类MyClass,其中T:OneType
{
T MyObj
{get;set;}
公共MyClass(T obj)
{
}
公共静态MyClass工厂(T vd)
其中T:OneType
{
if(vd为两种类型)
{
返回(MyClass)(object)新的子类(vd为TwoType);
}
返回null;
}
公共字符串工作
{
获取{返回this.GetType().Name;}
}
}
公共类TwoType:OneType
{
}
公共类子类:MyClass
{
公共子类(TwoType obj)
:基底(obj)
{
}
}
在我的表格中,我有:

        MyClass<TwoType> t = MyClass<TwoType>.Factory<TwoType>(new TwoType());

        MessageBox.Show(t.Working);
MyClass t=MyClass.Factory(新的TwoType());
MessageBox.Show(t.Working);

编辑:显然,正如Eric在上面指出的那样,这并不理想。虽然这段代码在技术上可以编译并工作到一定程度,但您可能希望找到一个更好的总体解决方案。

令人惊讶的是,我通过这样编写代码来实现它:

return (new SubClass(vd as TwoType) as MyClass<T>);
return(新的子类(vd作为TwoType)作为MyClass);

return(MyClass)(object)新的子类((TwoType)(object)vd);
但是,

return (MyClass<T>)new SubClass((TwoType)vd );
return(MyClass)新的子类((TwoType)vd);
不起作用


as
()
铸造中似乎存在差异

我知道这是一个老生常谈的问题,但我认为它也应该回答为什么这是不可能的(如果不使用各种丑陋的解决方法)

在某种意义上,当使用泛型时,您就是在使用模板代码。编译器对您的代码使用更严格的规则集,因为它也必须在编译最终版本的运行时进行编译

因此,当您使用泛型创建类或方法时,它们必须可编译为任何可能的组合
return (new SubClass(vd as TwoType) as MyClass<T>);
return (MyClass<T>)(object)new SubClass((TwoType)(object)vd );
return (MyClass<T>)new SubClass((TwoType)vd );
public class Super { }

public class Child : Super { }

public class Sister : Super { }
public void InvalidMethod<T>(T input)
  where T : Super
{
  Child castedReference = null;
  if (input is Child)
  {
    // This intuitively ought to be valid C#, but generates a compiletime error
    castedReference = (Child)input;
  }
  // Do stuff...
}
public void InvalidMethod(Sister input)
{
  Child castedReference = null;
  // Following 'if' is never true
  if (input is Child)
  {
    // Following statement is invalid C#
    castedReference = (Child)input;
  }
  // Do stuff...
}