【问题标题】:Convert Generic Class to Its Inherited Class将泛型类转换为其继承类
【发布时间】:2018-08-20 14:40:51
【问题描述】:

我正在重构一个旧库,其中包含如下类:

public class Template  //Old one
{
    public double Value { get; set; }
}

现在最好提供更多的灵活性来允许用户定义Value 类型。于是我把Template改成了泛型类:

public class Template<T>
{
    public T Value { get; set; }
}

此更改打破了其他人对旧 Template 类的用法,因此我尝试添加向后兼容性(允许其他人以旧方式使用我的库):

public class Template: Template<double> { }

但它也需要对库进行大量更改。尤其是在使用旧的Template的地方,比如:

public class AnotherClassInLibrary<T>
{
    public Template<T> API() {...}
}

public class AnotherClassInLibrary : AnotherClassInLibrary<double>
{
    // This class is also defined for the old way of using the library
    public Template API()
    {
         return base.API();
         // Error here: Template<double> cannot be implicitly convert to Template.
    }
}

所以问题来了:新的Template 是从Template&lt;double&gt; 继承的,所以在这里转换不是一个好的/有效的做法。是否有解决方法来保持AnotherClassInLibrary 向后兼容而不重写API() 代码两次?

附:我真的希望 C# 在 C++ 中有类似 typedef 的东西。但是the answer is NO

【问题讨论】:

  • 你的非泛型 Template 类在哪里?
  • 如果另一个类型实现了Template&lt;double&gt;,你还希望该强制转换有效吗?
  • “虽然Template 完全是Template&lt;double&gt; - 不,不是,它是继承链更深的一类。
  • 您正在尝试从父母投射到孩子。如果你有Template&lt;int&gt; a 怎么办?你还想投吗?
  • 仅仅因为你没有提供新功能,这仍然不会使Template 成为@987654341 类型的某种同义词别名 @。它们是不同的类型。

标签: c# generics casting


【解决方案1】:

尽管您的 Template 类继承自 Template&lt;double&gt;,但它并不相同。假设您将更改 Template 的定义,如下所示:

public class Template : Template<double>
{
    public int AddedMember { get; set; }
}

现在,您尝试执行前面的代码。您正在尝试将具有某些属性的类转换为具有更多属性的类 - 您将访问内存中不正确的位置!

您总是可以将一个类转换为它的基类,因为驱动类包含所有基类的成员。因此,不需要显式转换。但是,当您尝试将基类转换为驱动类时,只有当基类变量指向驱动类的实例时,转换才会成功,因此驱动类的所有成员都存在。如果没有,InvalidCastException 将在运行时抛出。因此,需要显式转换(因为使用显式转换的准则之一是在转换可能失败时使用显式转换)。

因此,如果您将代码更改为变量 a(Template&lt;double&gt; 类型),则转换将成功:

Template<double> a = new Template();
Template b = (Template)a; // No exception will thrown

最后的代码会成功,因为变量a 指向Template 的实例(而不是Template&lt;double&gt;),所以我们确定(在运行时)Template 的所有成员都存在并且不存在会发生错误。

编辑:

在你说出你的要求后,我可以帮你。你想要什么?您想启用“模板”到“模板”的转换 - 这可以通过 [用户定义的转换](https://docs.microsoft.com/en-us/dotnet/csharp/programming-guide/statements-expressions -运营商/转换运营商)。

但是,不允许在驱动类和基类之间进行自定义转换。所以,有两种解决方案:

  1. Template 不会继承 Template&lt;double&gt;,如下:
    public class Template<T>
    {
        public T Value { get; set; }
    }
    
    public class Template
    {
        public double Value { get; set; }
        
        public static explicit operator Template(Template<double> generic) // Or implicit instead of explicit
        {
            return new Template { Value = generic.Value };
        }
    }

    var generic = new Template<double> { Value = 1234.56 };
    var nongeneric = (Template)generic; // Or Template nongeneric = generic; if the conversion defined as implicit
  1. 您不会定义自定义转换,而是定义一个常规方法(我定义了一个构造函数):
    public class Template<T>
    {
        public T Value { get; set; }
    }
    
    public class Template : Template<double>
    {
        public Template(Template<double> generic)
        {
            this.Value = generic.Value;
        }
    }
    
    var generic = new Template<double> { Value = 1234.56 };
    var nongeneric = new Template(generic);

【讨论】:

  • 感谢您的回答。 Template 和 Template 之间有很多共享方法,所以昨天经过多次尝试,我选择了第二种方案。希望您的回答能帮助其他有类似问题的人:D
猜你喜欢
  • 1970-01-01
  • 1970-01-01
  • 2011-08-19
  • 1970-01-01
  • 2012-11-29
  • 1970-01-01
  • 1970-01-01
  • 1970-01-01
  • 1970-01-01
相关资源
最近更新 更多