为什么我不能在Base中将一个隐式运算符写入C#中的Derived类?
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); } }
这不行。 为什么不允许?
我可以写出有必要的逻辑,特别是在从基础转换为派生的逻辑时。
编辑:更改了问题的标题
因为已经有从Derived
到BaseClass
的隐式转换,并且反过来没有任何意义。
关于后者:如果您的Base
对象可以隐式转换为Derived
– 为什么它们不是Derived
对象?
标准的强制性引用:
6.1.6隐式引用转换
隐式引用转换是:
- […]
- 从任何类型S到任何类型类型T,只要S来自T.
这表示存在隐式转换Derived
=> Base
,我们都知道。
6.2.4显式引用转换
显式引用转换是:
- […]
- 从任何类型S到任何类型类型T,只要S是T的基类。
- […]
这表示已经有一个显式转换Base
=> Derived
(这是允许你在运行时尝试向下转换的东西)。
6.4.1允许的用户定义转换
C#仅允许声明某些用户定义的转换。 特别是,无法重新定义已存在的隐式或显式转换。
这说明由于两种兴趣转换已经由语言定义,因此您无法重新定义它们。
因为它会扰乱多态性的系统。
一般规则是将对象转换为自己的类型的结果是原始对象。 如果BaseType
类型的存储位置包含DerivedType
的实例, DerivedType
原始类型转换为DerivedType
应该告诉编译器使用DerivedType
的成员,但实际上不应该对该对象“执行”任何操作。 如果允许自定义的从基础到派生的转换运算符,则有必要:(1)具有转换为实例的自有类型操作有时会产生新对象,有时不产生,或者(2)具有派生类型存储在基本类型存储位置中的对象与基本类型或不相关类型的对象的行为基本不同,没有明确可见的类型检查代码,这将使它这样做。 虽然有时人们可能希望有一个方法,给定一个base-type参数,可能会返回一个新的派生类型对象,或者 – 如果给定一个派生类型的实例,只需返回它未经修改,通常更好的方法是有这样的东西“看起来”像一个方法而不是类型转换。
顺便说一下,有一种情况是编译器可以在“基本类型”和“派生类型”之间允许用户定义的类型转换而没有上述歧义:当其中一个类型是结构时。 虽然C#假装值类型inheritance自ValueType
,但每个值类型定义确实定义了两件事:一个派生自ValueType
的堆对象类型,以及一个存储位置集合,它不是一个对象,也不是从任何东西派生的。 C#定义了从后一种类型到前者的隐式转换运算符,以及从前者到后者的显式转换运算符。 由于堆对象和存储位置集合之间的转换永远不会引用保留,因此允许在此类上下文中使用用户定义的转换运算符不会导致混乱的inheritance语义。 这种转换的唯一困难是使用它们的值类型要么作为generics类型不可用,要么如果作为generics类型传递则会失去它们的特殊行为。
要合成,您可以将Derived对象转换为BaseClass对象,而无需编写任何代码:
BaseClass baseClass = new BaseClass(""); Derived derived = new Derived(""); baseClass = (BaseClass)derived;
但是你无法将Derived中的强制转换重写为BaseClass。
对于其他演员,从BaseClass到Derived,它没那么有意义。
总之,只有在两个类之间没有inheritance关系时,才能重新定义强制转换。
我需要能够从基础对象创建派生对象,以便捕获基类没有的其他信息。 出于我的目的,构造一个派生对象,其中包含从基础对象复制的所有字段。 我使用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(); } };