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);
}
}