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

C# 在C中实现一个行为类似于值类型的引用类型,c#,pass-by-reference,pass-by-value,C#,Pass By Reference,Pass By Value,我想在C中实现一个3D向量类型。它将有重载操作符用于加法、减法、点积等等。使用模式要求它的行为类似于值类型——例如,我需要一个assimment语句来复制数据。因此,出于这个和其他原因,我似乎应该使用struct 但是我还想要一个UnitVector类型,表示一个长度为1的向量。假设UnitVector应该继承自Vector。但只有当Vector和UnitVector是类而不是结构时,继承才起作用 我该怎么办?有没有办法让Vector和UnitVector类像string那样表现为值类型?或者是

我想在C中实现一个3D向量类型。它将有重载操作符用于加法、减法、点积等等。使用模式要求它的行为类似于值类型——例如,我需要一个assimment语句来复制数据。因此,出于这个和其他原因,我似乎应该使用struct

但是我还想要一个UnitVector类型,表示一个长度为1的向量。假设UnitVector应该继承自Vector。但只有当Vector和UnitVector是类而不是结构时,继承才起作用


我该怎么办?有没有办法让Vector和UnitVector类像string那样表现为值类型?或者是否有某种可能的方案,使得Vector和UnitVector都独立地实现某种IVector接口?正在寻找建议的方法。

在指定对该类型的引用时,无法创建一个副本。因此,如果类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子类,不变性也是一件非常好的事情。为什么?想象一下向量的分量会发生变化。然后,单位向量的分量将能够以相同的方式改变,否则它将违反Liskov替换原理。但这意味着你可以改变单位向量的长度。哎呀。这也被称为


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

无法使C引用在指定该类型的引用时创建副本。因此,如果类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子类,不变性也是一件非常好的事情。为什么?想象一下向量的分量会发生变化。然后,单位向量的分量将能够以相同的方式改变,否则它将违反Liskov替换原理。但这意味着你可以改变单位向量的长度。哎呀。这也被称为


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

有没有办法使vector和UnitVector类的行为像string那样的值类型?让它像字符串一样不可变?@dasblinkenlight-UnitVector不是常量。有无限多个长度为1的向量。所以,我不理解你的建议。@bubba:最简单的方法可能是使类在一般情况下是不可变的。因此,创建一个实例意味着它在构建之后永远不会改变。应用加法/减法/etc运算符将创建一个新实例。如果要更改另一个具有此向量的对象,则不必更改向量上的值,而是用新构造的向量完全替换该向量。@bubba一个不可变类中可能有无限多个实例具有相同的值,在创建之后,它们无法转换为具有不同值的实例。有没有办法使Vector和UnitVector类像string那样表现为值类型?让它像字符串一样不可变?@dasblinkenlight-UnitVector不是常量。有无限多个长度为1的向量。所以,我不理解你的建议。@bubba:最简单的方法可能是使类在一般情况下是不可变的。因此,创建一个实例意味着它在构建之后永远不会改变。应用加法/减法/etc运算符将创建一个新实例。如果要更改另一个具有此向量的对象,则不必更改向量上的值,而是用新构造的向量完全替换该向量。@bubba可能有无限多个实例
对于一个具有相同值的不可变类,它们在创建后无法更改为具有不同值的实例。因此,按照我的理解,使用此方案,我唯一失去的是执行简单直接赋值的能力。我不能只写v2=v1;我必须写v2=v1.Copy或v2=newvectorv1。对吗?当我将向量传递给函数时会发生什么?我想我必须避免任何公共属性也会改变向量的值,对吧。如果我想有一个公共属性X,它允许我写v.X=7之类的东西,那么在内部,属性设置器必须生成一个新的向量。这就是我的想法吗?现在,我用一个结构来表示向量,我没有单位向量。我的向量是可变的。我一直在读易变结构是邪恶的,但我不明白为什么。我必须写v2=v1。复制->不,你可以只写v2=v1。Vector3d是一种正常的引用类型,因此v2和v1将简单地指向同一对象。同样的事情,如果你将一个Vector3d传递给一个函数,该函数将与你传递的同一个对象一起工作。事实上,直接复制一个不可变值类型通常是毫无意义的,因为您可以只使用原始值。是的,在Vector3d中不能有任何在构造函数之外更改X、Y或Z的内容;你必须让你的函数返回新的Vector3ds。这应该都很熟悉,看看字符串。不能更改字符串,string.ToUpper等方法将返回新字符串。易变性本身并不是坏事,但程序的活动部分越少,就越容易理解它。想象一下,将一个可变的Vector3d传递给一个函数,该函数在某些计算中意外地更改了它,因此原始值将被更改。当然,通过让每个人都使用自己的值副本,结构以不同的方式防止这种情况发生。因此,按照我的理解,在这个方案中,我唯一失去的是执行简单直接赋值的能力。我不能只写v2=v1;我必须写v2=v1.Copy或v2=newvectorv1。对吗?当我将向量传递给函数时会发生什么?我想我必须避免任何公共属性也会改变向量的值,对吧。如果我想有一个公共属性X,它允许我写v.X=7之类的东西,那么在内部,属性设置器必须生成一个新的向量。这就是我的想法吗?现在,我用一个结构来表示向量,我没有单位向量。我的向量是可变的。我一直在读易变结构是邪恶的,但我不明白为什么。我必须写v2=v1。复制->不,你可以只写v2=v1。Vector3d是一种正常的引用类型,因此v2和v1将简单地指向同一对象。同样的事情,如果你将一个Vector3d传递给一个函数,该函数将与你传递的同一个对象一起工作。事实上,直接复制一个不可变值类型通常是毫无意义的,因为您可以只使用原始值。是的,在Vector3d中不能有任何在构造函数之外更改X、Y或Z的内容;你必须让你的函数返回新的Vector3ds。这应该都很熟悉,看看字符串。不能更改字符串,string.ToUpper等方法将返回新字符串。易变性本身并不是坏事,但程序的活动部分越少,就越容易理解它。想象一下,将一个可变的Vector3d传递给一个函数,该函数在某些计算中意外地更改了它,因此原始值将被更改。当然,结构以不同的方式防止这种情况,让每个人都使用自己的值副本。