【问题标题】:Casting between classes with generics使用泛型在类之间进行转换
【发布时间】:2014-01-31 21:36:19
【问题描述】:

谁能解释一下为什么这段代码 sn-p 不起作用?为什么a不能转换为b? 我在考虑协变和逆变,但据我所知,这不适用于抽象类。

编译错误: 无法将类型“ConsoleApplication1.SVM”转换为“ConsoleApplication1.VMSBase”ConsoleApplication1\Program.cs

class Program
{
    static void Main(string[] args)
    {
        var a = new SVM();

        var b = (VMSBase<Model>)a;
    }
}

class SVM : VMSBase<SpecialModel>
{

}

class VMSBase<TS> : VMBase<TS> where TS : Model
{

}

class VMBase<T> where T : Model
{
}

class SpecialModel : Model
{

}

class Model
{

}

【问题讨论】:

  • 虽然我不能确定,因为这个话题变得令人困惑,我相信解决方案正在将VMSBase&lt;TS&gt; 更改为VMSBase&lt;out TS&gt;。如果这不起作用,则改为使用 in 修饰符。
  • 这是out修饰符上的documentation

标签: c# .net generics


【解决方案1】:

SVMVMSBase&lt;SpecialModel&gt; 的子类型,因此可以转换为一个。

但是VMSBase&lt;SpecialModel&gt;VMSBase&lt;Model&gt;之间没有多态关系,因为VMSBase&lt;T&gt;中的泛型类型参数T不变的


为了使VMSBase&lt;X&gt; 成为VMSBase&lt;Y&gt; 的子类型(其中XY 的子类型),T 必须是协变的。您可以使用 out 关键字将其标记为协变:VMSBase&lt;out T&gt;。但是,这会强制您将类型 T only 用于来自所有成员(方法、属性等)的返回值,并 从不 作为输入值(方法参数)。

还有一个问题:c# 只允许接口上的变化。因此,您必须将 VMBase 和 VMSBase 都更改为接口。

class Program
{
    static void Main(string[] args)
    {
        SVM a = new SVM();
        var b = a as IVMSBase<Model>;
    }
}

class SVM : IVMSBase<SpecialModel> {}

interface IVMSBase<out TS> : IVMBase<TS> where TS : Model {}

interface IVMBase<out T> where T : Model {}

更多信息:Covariance and Contravariance FAQ

【讨论】:

    【解决方案2】:

    底线是VMSBase&lt;SpecialModel&gt;VMSBase&lt;Model&gt; 不同。

    这与无法编译的原因相同:

     List<ViewBase> list = new List<GridView>();
    

    虽然 GridView 继承自 ViewBase。

    这就是语言的工作方式,你可能会说泛型的限制。

    【讨论】:

      【解决方案3】:

      想象一下你可以合法地做演员表。现在想象我们定义了这个吃模型的方法:

      class VMSBase<TS> : VMBase<TS> where TS : Model
      {
          public void GobbleUpModel(TS model)
          {
      
          }
      }
      

      使用它,我们现在可以通过以下方式绕过类型安全(如果您以前没有见过它,您会感到惊讶):

      //SpecialModel2 is some other subclass of Model, not related to SpecialModel
      SpecialModel2 incompatibleModel;
      
      var a = new VMSBase<SpecialModel>();
      
      var b = (VMSBase<Model>)a;
      
      //forces a to gobble up a model that is incompatible with SpecialModel
      b.GobbleUpModel(incompatibleModel);
      

      【讨论】:

        【解决方案4】:

        泛型在 C# 中不是变体的原因是因为它可能导致键入问题:使用您的示例,假设 VMSBase 具有名为 MyProperty 的类型 T 的属性。如果可以进行强制转换,您可以执行以下操作:

        var a = new VMSBase<SpecialModel>();
        var b = (VMSBase<Model>) a;
        b.MyProperty = new Model();
        

        现在您只需将 b.MyProperty 的值设置为模型的实例;但这与 VMSBase 中预期的类型不一致,实际上是 SpecialModel。

        【讨论】:

          猜你喜欢
          • 1970-01-01
          • 1970-01-01
          • 1970-01-01
          • 1970-01-01
          • 1970-01-01
          • 1970-01-01
          • 1970-01-01
          • 1970-01-01
          • 2020-04-12
          相关资源
          最近更新 更多