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

C#混淆类层次结构中显式类型转换的必要性,c#,inheritance,casting,type-conversion,C#,Inheritance,Casting,Type Conversion,我有两个类向量和点,其中点是向量的子类。我有一个方法Scale用于Vectors(因此也用于Points),还有一个方法Clone用于Points,返回深度副本 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

我有两个类
向量
,其中
向量
的子类。我有一个方法
Scale
用于
Vector
s(因此也用于
Point
s),还有一个方法
Clone
用于
Point
s,返回深度副本

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;
但我的问题是,我不明白为什么这是必要的。 我认为编译器就是这样做的:

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

  • 那么问题是什么,为什么编译器不能推断我不需要任何强制转换
  • 我如何修复它,以便用户不必每次都进行强制转换?实现这一点的唯一方法是编写一个方法
    Point.Scale(double)
    ?我知道通过重定向到
    Vector.Scale(double)
    ,我可以使它成为一个单行程序,但由于新方法的附加文档注释,我将有额外的成本,因此这是不可取的
  • 这种强制转换是否只是一种形式主义,编译器不会抱怨,还是会在内部执行任何操作?我指的是任何像内存转移、分配/释放内存或任何计算之类的事情

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


有关问题,请参见下面的注释。

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


有关问题,请参见下面的注释。

这是因为推断类型是最后执行的函数的返回类型(最右边的函数,或者在您的示例中是
Scale()
方法,该方法返回的是
向量而不是


为了不必每次调用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;
    }
}

关于你的第三个问题:

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

因此,它不能保证返回的对象可以转换为
对象


您必须编写的cast在这里告诉编译器:“别担心,我知道这个对象是我指示给您的类型”

这是因为推断的类型是最后执行的函数的返回类型(最右边的,或者
Scale()
方法,它返回一个
向量,而不是


为了不必每次调用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;
    }
}

关于你的第三个问题:

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

因此,它不能保证返回的对象可以转换为
对象


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

Scale方法返回一个向量对象。不能将矢量对象指定给点参照。点继承自向量


此外,我建议您使用结构而不是类。这样就不需要克隆方法。

Scale方法返回向量对象。不能将矢量对象指定给点参照。点继承自向量


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

Scale
返回
Vector
,然后尝试分配它
;您可以简单地将
投射到
向量
,但不能将
向量
投射到
缩放
返回
向量
,并尝试分配它
;您可以简单地将
强制转换为
向量
,但不能将
向量
强制转换为
,然后使用返回的对象解除所有类型安全:p这是真的。我同意这不太理想。然后你就失去了返回对象的所有类型安全性:p这是真的。我同意这并不理想。完整的
向量
类有更多的方法,我需要它们是可变的,以避免为每次计算分配内存。例如,向量与矩阵相乘不应返回新向量,因为原始向量并不总是需要的,也可能被覆盖。如果必须保留旧的向量,我们可以使用
Clone
(我也在
vector
中实现了这一点)。@Kjara我建议您在问题中添加这些精确性,以便人们理解,这些类只是示例类,而真正的类则稍微复杂一点;)完整的
向量
类有更多的方法,我需要它们是可变的,以避免为每次计算分配内存。例如。
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);
    }
}
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);
    }
}