【问题标题】:Do I have to define every single operator?我必须定义每个运算符吗?
【发布时间】:2012-02-13 19:04:26
【问题描述】:

假设我有一个只有一个字段的结构:

public struct Angle
{
    public static readonly double RadiansPerDegree = Math.PI / 180;

    private readonly double _degrees;

    public Angle(double degrees)
    {
        _degrees = degrees;
    }

    public double Degrees
    {
        get { return _degrees; }
    }

    public double Radians
    {
        get { return _degrees * RadiansPerDegree; }
    }

    public static Angle FromDegrees(double value)
    {
        return new Angle(value);
    }

    public static Angle FromRadians(double value)
    {
        return new Angle(value / RadiansPerDegree);
    }
}

这很好用,直到我想做这样的事情:

var alpha = Angle.FromDegrees(90);
var beta = Angle.FromDegrees(100);
var inequality = alpha > beta;
var sum = alpha + beta;
var negation = -alpha;
//etc.

所以,我实现了IEquatable<in T> 和IComparable<in T>,但仍然没有启用任何运算符(甚至没有启用任何运算符(甚至==、<、>= 等)。

所以,我开始提供运算符重载。

例如:

public static Angle operator +(Angle a, Angle b)
{
    return new Angle(a._degrees + b._degrees);
}

public static Angle operator -(Angle a)
{
    return new Angle(-a._degrees);
}

public static bool operator >(Angle a, Angle b)
{
    return a._degrees > b._degrees;
}

这很奏效,但是,当我查看所有可能超载的运算符 (+, -, !, ~, ++, --, true, false, +, -, *, /, %, &, |, ^, <<, >>, ==, !=, <, >, <=, >=) 时,我开始觉得一定有更好的方法。毕竟struct只包含一个字段,而那个字段是值类型。

有没有办法一次性启用double的所有运算符?还是我真的必须手动输入我可能想要支持的每个运算符?

(即使我有两个或三个字段,我仍然希望能够批量添加运算符...)

【问题讨论】:

  • 您可能会添加隐式转换到(也可能来自)double 类型来做到这一点。不过,我脑海中的某些事情告诉我,这可能不是一个好主意,可能会导致模棱两可。
  • 如果您真的只是为了度数和弧度转换而这样做,为什么不只使用一个实现这些功能的静态 MathHelper 类并使用 double 而不是创建一个重新发明另一个类的类不同的名字?
  • @Jeff Mercado,实际上,我认为隐式转换可能正是我正在寻找的。您会考虑将您的评论转换为答案吗?另外,如果真的有缺点,如果您能对此进行扩展,我将不胜感激。谢谢。
  • @DanM:这有点问题,我真的不知道这是否有不利之处,我有一种奇怪的感觉。这让我不愿意建议作为答案。
  • @Jeff,很公平,尽管让它成为一个答案会给社区一个更好的参与机会。看看msdn.microsoft.com/en-us/library/z5z9kes2.aspx,这个特定的例子似乎符合隐式的预期用法投掷。您应该能够在任何双精度和任何角度之间进行转换(例如,Angle a = -720 将被解释为 0 度或 0 弧度,没有必要例外)。如果结构是Distance 而不是Angle,那么它就不会那么简单了。你能有负距离吗?

标签: c# .net struct operators operator-overloading


【解决方案1】:

重载运算符的要点是定义如何使用这些运算符添加操作自定义类型的对象,因此如果您的第二个字段是字符串数组,您希望 ++ 运算符如何自动实现?没有明智的答案,特别是因为我们不知道对象的上下文或它的用法,所以答案是是的,你必须自己重载运算符。

作为记录,如果你真的只需要一个字段,而且它只是一个双精度字段,那么首先不要使用结构,除非你需要重载运算符来执行一些其他操作,而不是默认情况下——这显然是过度设计的案例!

【讨论】:

  • Angle 示例是一个简化的例子,但我的目标是非常清楚单位是什么,并将单位之间的必要转换封装到一个单一类型中。所以,如果我有一个Angle,那么单位就没有歧义,我只需抓住我需要的任何一个。我的问题是,我希望有某种方法可以告诉编译器我希望结构的行为就像它是所有操作的基础字段一样。 @Jeff Mercado 建议使用隐式强制转换似乎可以实现这一点,但他似乎担心可能会产生意想不到的后果。
  • 一旦类型比原始类型更复杂,您就会看到系统无法为相关数据生成有意义的操作。至少你知道你在做什么!我建议您可以在诸如 Vim 之类的 IDE 中创建一个宏来为您的结构生成所有运算符,如果您需要多次执行此操作。
  • 我认为在我为这个特定项目预见的所有情况下,我可以将整个类型隐式转换为单个本机值类型以执行操作,但如果我遇到更复杂的情况,生成运算符重载的宏观想法听起来不错。
  • 啊,在编写一些测试时,我遇到了使用隐式转换的问题:两个Angles 的总和是double。您可以显式转换回Angle(或将结果设置为已声明为Angle 的变量),但这并不像定义单个运算符那样自然。所以,看来我只能硬着头皮了。
  • 是的,从长远来看,恐怕连选角都会变得丑陋和尴尬!正如其他人所指出的,在这种情况下最好的办法就是创建一些实用函数。
【解决方案2】:

在大多数情况下,我同意 LaceySnr:它实际上不适用于返回新对象的任何运算符(例如 +、* 等)。对于比较器,它可以工作(因为可能有一个注释说“在所有比较器操作中使用这个方法的返回值来代替这个对象”),但我不知道这样的事情。

我不确定 C# 中注释的限制是什么,但可以创建一个这样做的(对于返回 bool 的运算符),但除非您非常计划使用它,否则我对此表示怀疑值得您花时间。

话虽如此,如果您有一个对象,它只将一个参数带入构造函数,并且该参数是方法的返回值,那么也应该可以这样做。

当然,要做到这一切都需要一些非常极端的班级修补,我真的无法提供建议......

【讨论】:

    【解决方案3】:

    是的,您必须定义要使用的每个运算符。编译器无法知道您希望每个运算符做什么,除了彼此否定的运算符(甚至那些可能不一定很明显;如果您想模仿标准 SQL 空行为,其中 ==而!= 与 null 相比会返回 false?)。

    【讨论】:

      猜你喜欢
      • 2010-09-18
      • 2017-10-11
      • 2012-11-18
      • 2015-12-07
      • 1970-01-01
      • 1970-01-01
      • 2011-02-25
      • 2020-10-15
      • 1970-01-01
      相关资源
      最近更新 更多