【问题标题】:Why can't I write an implicit operator from a Base class to a Derived class in C#?为什么我不能在 C# 中将隐式运算符从基类写入派生类?
【发布时间】:2012-06-06 20:43:47
【问题描述】:
public class Derived : BaseClass
{
    public Derived(string name) : base(name) {}

    public static implicit operator BaseClass(Derived derived)
    {
        return new BaseClass(derived.ColorHex);
    }

    public static implicit operator Derived(BaseClass baseclass)
    {
        return new Derived(baseclass.name);
    }
}

这行不通。为什么不允许?
可以潜在地编写必要的逻辑以使其有意义,尤其是在从基础转换为派生逻辑时。

编辑:更改了问题的标题

【问题讨论】:

  • 因为转换已经存在,你不能增加它。
  • 当您想要对BaseClass 类型的对象进行一般引用时会发生什么?如果您按照定义对BaseClass 有隐式运算符,那么您实际上将“切片”对象并丢失任何运行时信息。您将无法调用任何被覆盖的虚拟方法(如果有的话)。

标签: c# operators


【解决方案1】:

因为已经存在从DerivedBaseClass 的隐式转换,反之则没有任何意义。

关于后者:如果您的 Base 对象旨在隐式转换为 Derived - 为什么它们不是 Derived 对象?

standard 中的强制引用:

6.1.6 隐式引用转换

隐式引用转换为:

  • [...]
  • 从任何类类型 S 到任何类类型 T,只要 S 是 源自 T。

这表示有一个隐式转换 Derived => Base,众所周知。

6.2.4 显式引用转换

显式引用转换为:

  • [...]
  • 从任何类类型 S 到任何类类型 T,前提是 S 是 T 的基类。
  • [...]

这表示已经有一个显式转换 Base => Derived(这使您可以在运行时尝试向下转换)。

6.4.1 允许的用户定义转换

C# 只允许声明某些用户定义的转换。 在 特别是,不可能重新定义已经存在的 隐式或显式转换。

这表示由于感兴趣的两个转换已经由语言定义,您无法重新定义它们。

【讨论】:

  • 因为它们来自我无法控制的地方,例如。
  • @Stephane:你可以控制Deriveds。那么,如果您需要它们,为什么不预先从Bases 中创建Deriveds 呢?
【解决方案2】:

因为它会干扰多态性的语义。

【讨论】:

    【解决方案3】:

    有一个一般规则,将对象转换为自己的类型的结果是原始对象。如果BaseType 类型的存储位置包含DerivedType 的实例,则从原始类型转换为DerivedType 应该告诉编译器使用DerivedType 的成员,但实际上不应该对目的。如果允许自定义基到派生的转换运算符,则有必要:(1)有一个转换为实例的自身类型操作有时会产生一个新对象,有时不会,或者(2)有一个派生类型存储在基本类型存储位置的对象的行为与基本类型或不相关类型的对象大不相同,没有清晰可见的类型检查代码可以做到这一点。虽然有时人们可能想要一个方法,给定一个基类型参数,它可能会返回一个新的派生类型对象,或者——如果给定派生类型的一个实例,只需不加修改地返回它,通常最好有这样一个“看起来”像方法而不是类型转换的东西。

    顺便说一句,在一种情况下,编译器可以允许在“基本类型”和“派生类型”之间进行用户定义的类型转换,而不会出现上述歧义:当其中一种类型是结构时。尽管 C# 假设​​值类型继承自 ValueType,但每个值类型定义实际上定义了两件事:派生自 ValueType 的堆对象类型,以及不是对象且不派生的存储位置集合从任何东西。 C# 定义了从后者类型到前者的隐式转换运算符,以及从前者到后者的显式转换运算符。由于堆对象和存储位置集合之间的转换永远不会保留引用,因此允许在这种上下文中使用用户定义的转换运算符不会导致继承语义混乱。这种转换的唯一困难是使用它们的值类型要么不能作为泛型类型使用,要么如果作为泛型类型传递就会失去它们的特殊行为。

    【讨论】:

    • 感谢您提供有用的精确度。
    【解决方案4】:

    要进行综合,您已经可以将 Derived 对象转换为 BaseClass 对象,而无需编写任何代码:

    BaseClass baseClass = new BaseClass("");
    Derived derived = new Derived("");
    
    baseClass = (BaseClass)derived;
    

    但您不能将转换从 Derived 重写为 BaseClass。

    对于其他演员,从 BaseClass 到 Derived,它没有多大意义。

    总而言之,只有在 2 个类之间没有继承关系时,您才能重新定义强制转换。

    【讨论】:

      【解决方案5】:

      我需要能够从基对象创建派生对象,以便捕获基类没有的附加信息。出于我的目的,构造一个包含从基础对象复制的所有字段的派生对象是可以接受的。我使用 AutoMapper 让我的生活更轻松:

      class AnnotatedAlert : Alert
      {
          public string Color;
      
          // I don't know why C# doesn't allow user defined conversions from a base class
          // so I am creating a conversion constructor instead
          public AnnotatedAlert(Alert from)
          {
              AutoMapper.Mapper.Map(from, this);
          }
      
          static AnnotatedAlert()
          {
              AutoMapper.Mapper.CreateMap<Alert, AnnotatedAlert>();
          }
      };
      

      【讨论】:

        猜你喜欢
        • 2019-09-02
        • 2021-07-03
        • 1970-01-01
        • 1970-01-01
        • 2012-06-05
        • 1970-01-01
        • 1970-01-01
        • 2012-04-21
        • 1970-01-01
        相关资源
        最近更新 更多