来自基类的用户定义的转换运算符

Laz*_*zlo 64 c# casting

介绍

我知道"不允许在基类之间进行用户定义的转换".作为对此规则的解释,MSDN给出了"您不需要此运算符".

我确实理解不需要用户定义基类的转换,因为这显然是隐式完成的.但是,我确实需要基类转换.

在我目前的设计中,非托管代码的包装器,我使用指针存储在Entity类中.所有使用指针的类都派生自该Entity类,例如Body类.

因此我有:

方法A.

class Entity
{
    IntPtr Pointer;

    Entity(IntPtr pointer)
    {
        this.Pointer = pointer;
    }
}

class Body : Entity
{
    Body(IntPtr pointer) : base(pointer) { }

    explicit operator Body(Entity e)
    {
        return new Body(e.Pointer);
    }
}
Run Code Online (Sandbox Code Playgroud)

这个演员是非法的.(注意,我没有打扰写入访问器).没有它,编译器允许我这样做:

方法B.

(Body)myEntity
...
Run Code Online (Sandbox Code Playgroud)

但是,在运行时,我会得到一个例外,说这个演员是不可能的.

结论

因此,我需要基类进行用户定义的转换,而C#拒绝它给我.使用方法A,编译器会抱怨,但代码在运行时逻辑上可以工作.使用方法B,编译器不会抱怨,但代码在运行时显然会失败.

在这种情况下我觉得奇怪的是,MSDN告诉我我不需要这个运算符,并且编译器就像隐含的一样(方法B).我应该做些什么?

我知道我可以使用:

解决方案A.

class Body : Entity
{
    Body(IntPtr pointer) : base(pointer) { }

    static Body FromEntity(Entity e)
    {
        return new Body(e.Pointer);
    }
}
Run Code Online (Sandbox Code Playgroud)

解决方案B.

class Body : Entity
{
    Body(IntPtr pointer) : base(pointer) { }

    Body(Entity e) : base(e.Pointer) { }
}
Run Code Online (Sandbox Code Playgroud)

解决方案C.

class Entity
{
    IntPtr Pointer;

    Entity(IntPtr pointer)
    {
        this.Pointer = pointer;
    }

    Body ToBody()
    {
        return new Body(this.Pointer);
    }
}
Run Code Online (Sandbox Code Playgroud)

但老实说,这些语法的所有语法都很糟糕,实际上应该是强制转换.那么,任何方式使演员表运作?这是一个C#设计缺陷还是我错过了一个可能性?这就好像C#不信任我足以使用他们的演员系统编写我自己的基础到子转换.

Jon*_*eet 41

这不是设计缺陷.原因如下:

Entity entity = new Body();
Body body = (Body) entity;
Run Code Online (Sandbox Code Playgroud)

如果允许您在此处编写自己的用户定义转换,则会有两个有效的转换:尝试执行常规转换(这是一个引用转换,保留标识)和用户定义的转换.

哪个应该用?你真的想要这样做会有不同的东西吗?

// Reference conversion: preserves identity
Object entity = new Body();
Body body = (Body) entity;

// User-defined conversion: creates new instance
Entity entity = new Body();
Body body = (Body) entity;
Run Code Online (Sandbox Code Playgroud)

育!那种疯狂在于,IMO.不要忘记编译器在编译时根据所涉及的表达式的编译时类型决定这一点.

就个人而言,我会选择解决方案C - 甚至可能使它成为一种虚拟方法.如果您希望在可能的情况下保留身份,但在必要时创建新对象,那么这种方式Body 可以覆盖它以返回.this


Igo*_*aka 18

好吧,当你进行投射EntityBody,你并没有真正地投射到另一个,而是将其投射IntPtr到一个新的实体.

为什么不从中创建显式转换运算符IntPtr

public class Entity {
    public IntPtr Pointer;

    public Entity(IntPtr pointer) {
        this.Pointer = pointer;
    }
}

public class Body : Entity {
    Body(IntPtr pointer) : base(pointer) { }

    public static explicit operator Body(IntPtr ptr) {
        return new Body(ptr);
    }

    public static void Test() {
        Entity e = new Entity(new IntPtr());
        Body body = (Body)e.Pointer;
    }
}
Run Code Online (Sandbox Code Playgroud)


rob*_*yaw 9

你应该使用你的解决方案B(构造函数参数); 首先,这就是为什么使用其他提议的解决方案:

  • 解决方案A只是解决方案B的包装器;
  • 解决方案C是错误的(为什么基类知道如何将自身转换为任何子类?)

此外,如果Body该类要包含其他属性,那么在执行转换时应将这些属性初始化为什么?使用构造函数并初始化子类的属性要好得多,就像OO语言中的约定一样.

  • 对于知道如何将自身转换为特定子类的基类,我没有任何问题......尤其是虚拟.见证Object.ToString,以及扩展方法IEnumerable.ToList,IEnumerable.ToArray. (4认同)