C#混淆了类层次结构中显式类型转换的必要性

时间:2016-07-27 08:51:58

标签: c# inheritance casting type-conversion

我有两个班级VectorPoint,其中PointVector的子类。我有一个方法Scale用于Vector s(因此也用于Point s)和方法Clone用于Point,它返回一个深层副本。< / p>

public class Vector
{
    protected double x, y, z;

    public Vector(double x, double y, double z)
    {
        this.x = x; this.y = y; this.z = z;
    }

    public Vector Scale(double sc)
    {
        this.x = sc * x; this.y = sc * y; this.z = sc * z;
        return this;
    }
}

public class Point : Vector
{
    public Point(double x, double y, double z) : base(x, y, z) { }

    public Point Clone()
    {
        return new Point(this.x, this.y, this.z);
    }
}

(注意:实际代码比这更复杂,有更多方法,但这段摘录应足以说明问题。请不要在继承层次结构中建议任何更改或将类转换为结构。我有已经与我的同事评估了这些问题。)

现在我将以下内容写入我的主程序:

Point p = new Point(1, 2, 3);
Point q = p.Clone().Scale(2); // compile error complaining about missing cast

第二行可以通过以下方式修复:

Point q = p.Clone().Scale(2) as Point;

但我的问题是我不明白为什么这是必要的。 这就是我认为编译器的作用:

pPointp.Clone()是另一个Point。我们检查是否存在方法Point.Scale(double)。我们找不到一个,所以我们检查超类是否有这样的方法,即方法Vector.Scale(double)是否存在。我们调查Vector.Scale,发现它返回this。由于p.Clone()Point,我们知道p.Clone().Scale(2)的返回值将是p.Clone()本身(但已修改),因此它必须是Point。瞧,没有必要的演员。

  • 那么问题是什么,为什么编译器不能推断出我不需要任何演员?
  • 如何修复它,以便用户不必每次都进行投射?编写方法Point.Scale(double)是实现此目的的唯一方法吗?我知道我可以通过重定向到Vector.Scale(double)使其成为单行,但由于新方法的附加文档注释,我将有额外的费用,所以这是不可取的。
  • 这种演绎只是一种形式主义,编译器不会抱怨,或者实际上是否会在内部做任何事情?我的意思是像内存移位或分配/释放内存,还是任何计算?

3 个答案:

答案 0 :(得分:2)

这是因为推断类型是最后执行的函数的返回类型(在您的情况下最右边的一个或Scale()方法返回Vector而不是Point

每次你想要调用scale方法时都不需要进行强制转换,你可以使用泛型。

一个例子是:

public abstract class Scalable<T>
{
    protected double x, y, z;

    public T Scale(double sc)
    {
        this.x = sc * x; this.y = sc * y; this.z = sc * z;
        return this;
    }

    public Scalable(double x, double y, double z)
    {
        this.x = x; this.y = y; this.z = z;
    }
}

该类将定义使对象可伸缩所需的内容,它被定义为抽象,以便开发人员无法创建该类的实例,只能从中继承。

然后你的其他两个班级会相应改变:

public class Vector : Scalable<Vector>
{
    public Vector(double x, double y, double z) : base(x,y,z)
    {
    }
}

public class Point : Scalable<Point>
{
    public Point(double x, double y, double z) : base(x, y, z) { }

    public Point Clone()
    {
        return new Point(this.x, this.y, this.z);
    }
}

另一个更简单的方法就是定义一个scale方法,该方法调用Vector one并一次性转换:

public class Point : Vector
{
    public Point(double x, double y, double z) : base(x, y, z) { }

    public Point Clone()
    {
        return new Point(this.x, this.y, this.z);
    }

    public Point Scale(double sc)
    {
        return (Point)base.Scale(sc);
    }
}

对于你的第三个问题:

编译器不知道你在方法中做了什么,它只知道返回什么类型。

因此,它无法保证返回的对象可以转换为Point对象。

你必须写的演员在这里告诉编译器:“别担心,我知道这个对象是我告诉你的类型”

答案 1 :(得分:1)

您可以将Scale的返回类型更改为dynamic(取决于编译器的可用性):

public dynamic Scale(double sc)
{
    this.x = sc * x; this.y = sc * y; this.z = sc * z;
    return this;
}

请参阅以下评论以了解问题。

答案 2 :(得分:1)

Scale方法返回Vector对象。您不能将Vector对象分配给Point引用。 Point继承自Vector。

此外,我建议你使用结构而不是类。然后你不需要克隆方法。