【问题标题】:C# custom unary operators overloadingC# 自定义一元运算符重载
【发布时间】:2013-11-01 10:18:26
【问题描述】:

C# 类是否从基类继承自定义运算符?我正在尝试以下代码:

class Person
{
    public int Age;

    public static Person operator ++(Person p)
    {
        p.Age++;
        return p;
    }
}

class Agent : Person {   }

static void Main(string[] args)
{
    Person p = new Person { Age = 23 };
    Console.WriteLine ( "Before increment : {0}" , p.Age );

    p++;
    Console.WriteLine("After increment : {0}", p.Age);

    Agent agent = new Agent { Age = 25 };
    Console.WriteLine("Before increment : {0}", agent.Age);

    agent++;
    Console.WriteLine("After increment : {0}", agent.Age);

}

编译器告诉我他不能显式地从 Person 转换为 Agent。

我试过了:

Agent agent = new Agent();
Person person = agent ++

但出现相同的编译器消息。

【问题讨论】:

标签: c# operator-overloading


【解决方案1】:

您遇到的问题不是基于运算符是否被继承,而是由于运算符的返回类型。

但是,在分配运算符的情况下,您将遇到运算符的返回类型问题

x++ 是表达式的简写

x = x + 1

在您的情况下,您将返回 Person 并尝试将其分配给 Agent。 写出来就是

agent.Age++;
agent = (Person)agent; //the cast is implicit and a result of the return type

您只能分配给更通用的类型。因此,将 Agent 分配给 Person 会起作用,这就是编译器允许返回类型是实现类型的特化的原因(如下所示)。

你可以通过一些泛型技巧来完成你正在寻找的东西

class Person<T> where T : Person<T>, new()
{
    public int Age;

    //taking advantage of the fact that the return type might be a specialization
    //of the type implementing the operator
    public static T operator ++(Person<T> p)
    {
        return new T { Age = p.Age + 1 };   
    }
}

class Agent : Person<Agent> { }

//Only required if Person should be usable on it's own
class Person : Person<Person> { }

上面的操作符构造了一个新对象,对我来说它与++operator 的契约是内联的,但还需要一个默认构造函数,如果你希望你可以使用强制转换来代替。

public static T operator ++(Person<T> p)
{
    p.Age++;
    return (T)p;
}

运算符是否被继承取决于您如何定义继承。

C# 规范以不同于CLI specifications 的方式使用“继承”。 C# 规范使用继承的方式是在与 CLI 规范不兼容的运算符和静态方法的上下文中(即 C# 规范与 CLI 规范相矛盾,因此 C# 运行的平台不支持该规范)

要说明为什么我的钱花在“运算符不被继承”上是因为它们是静态方法的语法糖。

[SpecialName]
public static T op_increment(Person<T> p)
{
    return new T { Age = p.Age + 1 };   
}

这是一个静态方法的实现。根据 CLI 规范,静态方法不会被继承:

派生对象类型继承其基对象类型的所有实例和虚拟方法。它 不继承构造函数或静态方法。

下面是++ 操作符的实现

public static T operator ++(Person<T> p)
{
    return new T { Age = p.Age + 1 };   
}

这两个实现的 IL 相同

【讨论】:

  • 错了,看看我的回答
  • 我绑定了“Person person = agent ++”,但它不起作用。编译器抱怨同样的转换问题。
  • 这甚至不是有效的 C# 代码。一元运算符的参数类型必须是包含类型(在本例中为Person&lt;T&gt;,而不是T)。此答案中的代码实际上不会编译。
  • @RuneFS 已经定义好了,用相关链接查看我的回答。
  • @SriramSakthivel 您已链接到 c++ 规范
【解决方案2】:

操作符被实现为静态的,继承不适用于静态成员。

有些运算符可以使用派生类型,有些则不行。这是类型转换规则的问题。

但基本上,Person p = ...; p++; 无论如何都是一个非常糟糕的主意。当您在适当的地方使用运算符重载时,您不会遇到这样的问题。

【讨论】:

    【解决方案3】:

    更新:

    与其他成员一样,在基类中声明的运算符由派生类继承。因为运算符声明始终要求声明运算符的类或结构参与运算符的签名,所以在派生类中声明的运算符不可能隐藏在基类中声明的运算符。因此,在操作符声明中从不需要 new 修饰符,因此也永远不允许使用。

    以上引用来自c# Language specification


    确实如此,编译器会检查继承层次结构中定义的任何运算符重载。如果找到它会使用它

    考虑下面的代码

    class Person
    {
        public int Age;
        public static int operator +(Person p, Person p2)
        {
            return p.Age + p2.Age;
        }
    }
    class Agent : Person
    {
    
    }
    static void Main()
    {
        Person p = new Person { Age = 23 };
        Agent agent = new Agent { Age = 25 };
    
        int res = p + agent;//Result 48
        Console.WriteLine("Result is "+ res);
    }
    

    在您的情况下,编译器发现 ++ 运算符并尝试使用它,但问题在于返回类型。它返回基本类型,所以它不起作用。它不能分配给更多派生类型。

    这里是Demo

    【讨论】:

    • Agent是否继承了自定义算子?
    • 不,运算符不是继承的。操作数被转换。
    • @HenkHolterman 它是继承的。除了(=) 之外的所有运算符都是继承的。 Source检查第9个项目符号
    • @BrahimBoulkriat 是的,它是继承的。看看here
    • @SriramSakthivel 这是一个关于 C++ 中运算符重载的链接。
    【解决方案4】:

    丑陋/漂亮但不那么通用的解决方案:

    class Agent : Person 
    {
        public static Agent operator ++(Agent p)
        {
            p.Age++;
            return p;
        }
    }
    

    【讨论】:

      【解决方案5】:

      对于这种特殊情况,您可以使用类似

      class Person
      {
          public int Age;
          public static T IncrementAge<T>(T p) where T : Person
          {
              p.Age++;
              return p;
          }
      }
      

      很遗憾,以下方法不起作用:

          public static T operator ++<T>(T p) where T : Person
          {
              p.Age++;
              return p;
          }
      

      不过,直截了当的方法可能是这个:

          public void IncrementAge()
          {
              Age++;
          }
      

      【讨论】:

        猜你喜欢
        • 1970-01-01
        • 1970-01-01
        • 2013-08-15
        • 1970-01-01
        • 1970-01-01
        • 1970-01-01
        • 2011-09-18
        • 2010-10-21
        相关资源
        最近更新 更多