c# – 对运算符重载感到困惑

随着我在我的小数学库上的进步,我绊倒了C#和.NET Framework的某些方面,我在理解方面遇到了一些麻烦.

这次它是运算符重载,特别是术语重载本身.为什么称为重载?默认情况下,所有对象都具有所有运算符的实现吗?那就是:

public static object operator +(object o1, object o2)

在某处预定义并以某种方式?如果是这样,为什么然后如果我尝试o1 o2我得到编译时错误运算符”不能应用于’object’和’object’类型的操作数?这将以某种方式暗示默认情况下对象没有预定义的运算符,那么术语重载怎么样呢?

我问这个,因为在我的数学库中,使用3D几何元素,我有以下结构:Vector和Point.现在,在内部我想允许以下构造来创建向量:

static Vector operator -(Point p1, Point p2) {...}

由于这在数学上不是100%正确但在内部非常有用以减少代码混乱,我不想公开公开此运算符,所以我的初衷是简单地做:

internal static Vector operator -(Point p1, Point p2) {...}

令人惊讶的是(对我来说)我得到以下编译时错误:用户定义的运算符’Geometry.Vector(Geometry.Vector,Geometry.Vector)’必须声明为static和public“.

现在所有操作符必须公开的这种限制似乎对运算符的整个重载方面有一定的意义,但似乎它有点不一致:

>默认情况下,对象没有预定义的运算符:默认情况下我无法执行对象对象或myTpye myType无法事先明确定义运算符.
>定义运算符不会被描述为创建运算符,它被描述为重载,在某种程度上与第1点不一致(对我而言).
>你不能限制操作符的访问修饰符,它们必须是公共的,考虑到第1点这没有意义.但考虑到第2点,这种方式是有意义的.

有人可以用简单的术语解释我所做的这些混乱吗?

最佳答案

Why is it called overloading?

它被称为“重载”,因为它正在超载.当我们为这个东西提供两个可能的实现然后必须决定使用哪个(称为重载解析)时,我们“重载”一个东西.

当我们重载方法时,我们给出两个或多个具有给定名称的方法的实现.当我们重载运算符时,我们为具有给定语法的运算符提供两个或更多可能的实现.这是同一件事.

确保您不会因重写而混淆重载.重载只是在同一个声明空间中存在两个具有相同名称/语法的方法/运算符.覆盖处理在运行时如何填充虚拟方法槽的内容.

Do all objects by default have an implementation of all operators?

没有.

Is public static object operator +(object o1, object o2) predefined somewhere and somehow?

没有.

This would somehow imply that by default objects do not have predefined operators so then how come the term overloading?

我不明白这个问题.当然C#有预定义的运算符.

I don’t want to expose this operator publicly

然后不要使用运算符;制作私人,内部或受保护的方法.

C#的设计是运算符始终是一种类型的公共表面区域的一部分.让操作符具有取决于使用的可访问域的含义是非常令人困惑的.C#经过精心设计,成为一种“质量陷阱”语言,语言设计者的选择使您远离写作混乱,越野车,难以重构的程序.要求用户定义的运算符是公共的和静态的是那些微妙的设计点之一.

(1) Objects by default do not have predefined operators

当然可以;在各种对象上有数百个预定义的运算符.例如,对于添加,运算符有以下预定义重载:

int + int
uint + uint
long + long
ulong + ulong
double + double
float + float
decimal + decimal
enum + underlying  (for any enum type)
underlying + enum
int? + int?
uint? + uint?
long? + long?
ulong? + ulong?
double? + double?
float? + float?
decimal? + decimal?
enum? + underlying?
underlying? + enum?
string + string
object + string
string + object
delegate + delegate (for any delegate type)

有关所有其他运算符的所有预定义重载的列表,请参阅C#规范.

请注意,运算符的重载决策有两个阶段:首先,重载决策尝试查找唯一最佳的用户定义的重载;只有这样做才发现没有适用的候选者是重载决议考虑的预定义重载.

(2) Defining operators is not described as creating an operator, it’s described as overloading which somehow is inconsistent (to me) with point 1.

我不明白为什么你发现它不一致,或者,就此而言,你发现不一致的东西.术语“过载”一直用于描述操作符和方法;在这两种情况下,它意味着使用相同的语法来引用两个或更多不同的东西,然后通过“重载分辨率”解决歧义.方法和运算符重载决策算法的确切细节是不同的,但它们在整个算法中是相似的:首先识别候选集,然后删除不适用的候选者,然后更好的算法消除比另一个更差的适用候选者,然后a最佳性算法确定剩下的唯一最佳候选者(如果有的话).

(3) You cannot restrict access modifier of an operator, they have to be public, which does not make sense considering point 1. but sort of makes sense considering point 2.

我不明白第(3)点与点(1)或(2)有什么关系.运算符必须成为公共表面区域一部分的限制是为了防止当你在Apple课堂内时能够为动物添加水果的混乱局面,而不是当你在班级长颈鹿中时.

Operators are declared inside a class or struct and therefore “belong” to said type, they don’t float “belonging” to no given type. So what am I overloading exactly when I declare an operator in a class?

你的操作符正在超载.

That the same operator exists between ints does not mean I am overloading anything as that operator belongs to int. To me its the same as saying the Foo.Hello() and Bar.Hello(string hello) are overloads of Hello. They are not as they are declared in two separate types. What is the difference with operators?

你刚才准确地描述了这种差异.方法重载和操作重载的许多细节都不同.

如果你想采取Foo.Hello()和Bar.Hello(字符串)是Hello的“重载”的位置,这不是一个常见的位置,但它在逻辑上是一致的.

I was under the impression that you cannot change the access modifier when overloading.

你的印象是错的;覆盖虚拟方法时,无法更改访问修饰符.你把它与重载混淆了.

(我注意到有一种情况需要在覆盖虚拟方法时更改访问修饰符;你能推断出它是什么吗?)

I was also under the impression that you can not declare an operator without at least one of the operands being of the type in which you declare the operator.

这几乎是正确的.用户定义的运算符必须具有类型为T的操作数,其中T是封闭的类或结构类型,或者T?如果T是结构类型.

So how can a third class have access to a given operator while another third class can’t unless one belongs to an external assembly and the other doesn`t in which case I do not find it confusing at all and even useful?

你错误地描述了我的例子,这可能更清楚了.这是非法的:

public class Fruit 
{ 
    protected static Shape operator +(Fruit f, Animal a) { ... } 
}

因为这很奇怪:

public class Apple : Fruit
{ 
    ...
    Shape shape = this + giraffe; // Legal!
}
public class Giraffe : Animal
{
    ...
    Shape shape = apple + this; // Illegal!
}

这只是一个例子.一般来说,做一个奇怪的事情是,使运算符的重载决策取决于可访问性域,因此语言设计者确保通过要求用户定义的运算符公开来实现这一点.

I simply find overloading confusing in the context of operators.

很多人都这样做,包括编译器编写者.规范的用户定义的运算符部分非常难以解析,并且Microsoft实现是编译器错误的丰富来源,其中许多是我的错.

I don’t see why simply declaring operators in a type has to described differently from how you would describe declaring any other static method.

嗯,不同的东西是不同的;运算符在很多方面都与方法不同,包括它们的重载决策算法.

我从来没有特别喜欢C#具有可重载的运算符. C#功能是C中相同功能的设计稍好的版本,但在这两种语言中,我认为该功能的成本远远高于相应的用户利益.

谢天谢地,至少C#没有彻底滥用<<操作符惯用C的方式 - 当然它确实滥用和 - 代表.

转载注明原文:c# – 对运算符重载感到困惑 - 代码日志