在C#中实现一个行为类似于值类型的引用类型

时间:2014-07-05 01:30:49

标签: c# pass-by-reference pass-by-value

我想在C#中实现3D Vector类型。它将重载运算符,用于添加,减少,点积等。使用模式要求它的行为类似于值类型 - 例如,我想要一个assigment语句来复制数据。因此,出于这个(和其他)原因,我似乎应该使用结构。

但我也希望有一个UnitVector类型来表示长度为1的向量。据推测,UnitVector应该从Vector继承。但是,只有当Vector和UnitVector是类而不是结构时,继承才有效。

我该怎么办?有没有办法使Vector和UnitVector类的行为像值类型(如字符串一样)?或者是否有一些可能的方案,Vector和UnitVector都独立实现某种IVector接口?寻找建议的方法。

1 个答案:

答案 0 :(得分:4)

当分配对该类型的引用时,无法使C#引用创建副本。因此,如果class UnitVector : Vector方法是你想要的方式,那么复制就可以了。

然而,@ ta.speot.is已经指出了一个有用的方向:如果你使Vector成为一个不可变的类,你不必担心复制它 - 它无论如何都不能改变。如果您还为GetHashCode()Equals()添加实现以获取值相等语义,那么这是一个非常好的值类型。我实际上更喜欢这样的类来构造结构,尽管它们最终会在C#中输入更多内容。

public class Vector3d
{
    public readonly double X, Y, Z;

    public Vector3d(double x, double y, double z)
    {
        X=x; Y=y; Z=z;
    }

    public double Dot(Vector3d other)
    {
        return X*other.X + Y*other.Y + Z*other.Z;
    }

    public UnitVector3d Normalize()
    {
        return UnitVector3d.FromVector(this);
    }

    /* More operators like Times and Length, plus Equals and GetHashCode here */
}

public class UnitVector3d : Vector3d
{
    private UnitVector3d (double x, double y, double z) : base(x, y, z)
    {
        /* General constructor. Private so it can only be called by trusted
           functions because it could be used to construct non-unit UnitVectors */
    }

    public static UnitVector3d FromVector(Vector3d vec)
    {
        Vector3d normalized = vec.Times(1.0/vec.Length());
        return new UnitVector3d(normalized.X, normalized.Y, normalized.Z);
    }
}

如果您想使用UnitVector子类,那么不变性也是一件非常好的事情。为什么?想象一下,Vector的组件可能会发生变化。然后UnitVector的组件将能够以相同的方式改变(否则它将违反Liskov替换原则)。但这意味着您可以以改变其长度的方式更改UnitVector。哎呀。这也称为Circle-Ellipse-Problem

Vector永久不变可以避免这个问题。 UnitVector可能只是一个子类,没有任何额外的代码可用于表示已知长度为1的向量 - 您甚至可以覆盖那里的Length函数进行优化。