在C#中重载赋值运算符

76
我知道=运算符无法重载,但肯定有办法可以实现我的需求:
我正在创建表示量纲单位的类,因为我正在进行一些物理方面的工作。显然我不能从原始类型继承,但我希望我的类的行为与原始类型完全相同——只是我想让它们以不同的类型出现。
所以我应该能够这样做:
Velocity ms = 0;
ms = 17.4;
ms += 9.8;

我不确定如何实现这个。我想我只需编写一些类,就像这样:

class Power
{
    private Double Value { get; set; }

    //operator overloads for +, -, /, *, =, etc
}

但显然我无法重载赋值运算符。有没有什么方法可以实现这种行为?


你有没有看过 F# 的 度量单位 功能?它知道标准(ISO)单位,如米、千克和米/秒,并且它也可以进行单位计算。 - oɔɯǝɹ
1
当然,我现在正在使用它。它不知道ISO单位,而是您自己定义单位,例如 [<Measure>] type m; [<Measure>] type s,然后可以使用类似 let a = 9.8<m> / 4.3<s> 的东西,得到 val a : float<m/s> = 2.279069767 - Carson Myers
1
抱歉,我想说的是SI单位,这些单位在Microsoft.FSharp.Math.SI中预定义。请参见:http://blogs.msdn.com/b/andrewkennedy/archive/2008/09/02/units-of-measure-in-f-part-two-unit-conversions.aspx - oɔɯǝɹ
3个回答

110

听起来你应该使用一个struct而不是一个类...然后创建一个隐式转换运算符,以及各种加法等运算符。

以下是一些示例代码:

public struct Velocity
{
    private readonly double value;

    public Velocity(double value)
    {
        this.value = value;
    }

    public static implicit operator Velocity(double value)
    {
        return new Velocity(value);
    }

    public static Velocity operator +(Velocity first, Velocity second)
    {
        return new Velocity(first.value + second.value);
    }

    public static Velocity operator -(Velocity first, Velocity second)
    {
        return new Velocity(first.value - second.value);
    }

    // TODO: Overload == and !=, implement IEquatable<T>, override
    // Equals(object), GetHashCode and ToStrin
}

class Test
{
    static void Main()
    {
        Velocity ms = 0;
        ms = 17.4;
        // The statement below will perform a conversion of 9.8 to Velocity,
        // then call +(Velocity, Velocity)
        ms += 9.8;
    }
}

(顺便说一下......我不明白这真的代表了速度,因为肯定需要方向和大小。)


4
太完美了!谢谢。另外...你说得对。显然我的物理教授并没有把向量概念敲进我的脑子里 :) - Carson Myers
2
这个东西不能用类吗?我希望能够从ScalarUnit或者VectorUnit类继承,以免为每个单位重复编写所有内容,但是显然结构体无法继承。 - Carson Myers
8
@Henk:不,速度是一个标量,但速度具有方向。请参见http://en.wikipedia.org/wiki/Velocity - “它是一个矢量物理量;需要定义它的大小和方向。” - Jon Skeet
3
@Carson:你可以将它定义为类,但考虑到它代表的是一个相对基本的"值"而不是自然的"对象",将其定义为值类型更加合适。尽管如此,定义为类的代码基本上是相同的,因此你可以轻松地尝试两种方法。但你可能会在继承树中遇到难以表示的运算符问题。 - Jon Skeet
1
@Ben:没错 - 请阅读我评论的其余部分,基本上...并参见https://msdn.microsoft.com/en-us/library/ms229017.aspx - Jon Skeet
显示剩余7条评论

13
你可以创建隐式转换操作符。这里有一个很好的例子,MSDN页面上有介绍。
另外,将它们制作成不可变结构体也是一个好主意。这正是"基元类型"所做的,这也使得从它们继承变得不可能。你想要一个结构体,因为你想要值类型语义,而不是引用类型语义。并且你想让它们不可变,因为可变的值类型通常是一个坏主意

坏主意的链接已经失效,似乎只会带你到MS MVPs的主页。我认为https://codeblog.jonskeet.uk/2010/07/27/iterate-damn-you/是相关的文章? - Nemo

-9

我认为它不能被重载,因为C#类都是从Object派生的,所以它们基本上都是对象,当你使用赋值运算符时,你基本上只是引用另一个对象。另一方面,如果你使用结构体,那么你基本上需要所有的信息,所以当你使用=运算符时,所有字段都将被复制。

所以我会说,面对它,并实现一个名为Copy()的函数,你就没问题了 :-)


这与其他运算符有何不同?如何将两个对象的所有信息相加?我想直接分配给内部值,而不是覆盖整个对象。 - Carson Myers
1
我认为这是不可能被重载的,因为语言设计者认为这将是一个糟糕的想法。从技术上讲,它是可以实现的,但没有必要。 - R. Martinho Fernandes
1
不,我认为有一个原因。假设你有一个对象a和一个对象b。一旦你运行语句a=b,被a指向的对象实际上将被垃圾回收(因为没有对象再指向它),那么如何在这里实现赋值运算符?另一方面,所有其他运算符要么返回另一个对象(例如a + b),要么在当前对象不会丢失(即不会被垃圾回收)时实际更改当前对象(例如a += b)。 - Rafid
假设你有一个对象a和一个对象b。一旦你运行语句a=b,实际上发生的是在a上调用ToString方法,并将该字符串用作Console.WriteLine调用的第一个参数,而b则是第二个参数。这很愚蠢和丑陋,但这可能是“=”符号选择的含义。它只是一个符号,在C#中具有固定的含义。顺便说一下,C++允许重载该运算符。 - R. Martinho Fernandes
3
运行时可始终知道何时更改了引用,因为运行时需要运行更改它的代码。此外,请仔细查看语言本身。设计师已经决定使用重载 = 的特殊实例:属性设置器。让 Xfoo 的一个属性。在 foo.X = 3 中,编译器通过调用 foo.set_X(3) 来替换 = 符号。您已经可以定义一个 public static T op_Assign(ref T assigned, T assignee) 方法。唯一剩下的就是让编译器将 = 替换为对该方法的调用。 - R. Martinho Fernandes
显示剩余3条评论

网页内容由stack overflow 提供, 点击上面的
可以查看英文原文,
原文链接