2

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 ++

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

4

5 回答 5

6

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

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

表达式x++是一个简写形式

x = x + 1

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

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

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

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

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> { }

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

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

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

C# 规范以不同于CLI 规范的方式使用“继承” 。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 是相同的

于 2013-11-01T10:21:15.463 回答
2

运算符作为静态实现,继承不适用于静态成员。

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

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

于 2013-11-01T10:24:15.120 回答
2

更新:

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

以上引用来自c# 语言规范


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

考虑以下代码

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);
}

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

这是演示

于 2013-11-01T10:25:42.423 回答
0

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

class Agent : Person 
{
    public static Agent operator ++(Agent p)
    {
        p.Age++;
        return p;
    }
}
于 2013-11-01T10:33:19.593 回答
0

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

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++;
    }
于 2013-11-01T10:37:25.800 回答