为什么叫重载?
它被称为“重载”,因为它是重载。当我们为该事物提供两种可能的实现时,我们“重载”了该事物,然后必须决定使用哪个(这称为重载决议)。
当我们重载方法时,我们会给出具有给定名称的方法的两个或多个实现。当我们重载运算符时,我们会为具有给定语法的运算符提供两个或多个可能的实现。这是同一件事。
确保您没有将重载与覆盖混淆。重载只是在同一个声明空间中存在两个具有相同名称/语法的方法/运算符。覆盖处理如何在运行时填充虚拟方法槽的内容。
默认情况下,所有对象都具有所有运算符的实现吗?
不。
在public static object operator +(object o1, object o2)
某处以某种方式预定义?
不。
这会以某种方式暗示默认情况下对象没有预定义的运算符,那么重载一词是怎么来的?
我不明白这个问题。当然 C# 有预定义的运算符。
我不想公开这个运营商
然后不要使用运算符;创建一个私有的、内部的或受保护的方法。
C# 的设计是运算符始终是类型的公共表面区域的一部分。具有取决于使用发生在哪个可访问域中的含义的运算符是非常令人困惑的。C# 被精心设计为“质量坑”语言,语言设计者的选择使您远离编写混乱,有缺陷的,难以重构的程序。要求用户定义的操作符是公共的和静态的是这些微妙的设计点之一。
(1) 对象默认没有预定义的操作符
当然可以;在各种对象上有数百个预定义的运算符。此外,例如,有以下预定义的 operator 重载+
:
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) 定义运算符没有被描述为创建一个运算符,它被描述为重载,它在某种程度上与第 1 点不一致(对我来说)。
我不明白你为什么觉得它不一致,或者,就此而言,你觉得不一致。术语“重载”始终用于描述运算符和方法。在这两种情况下,这意味着使用相同的语法来引用两个或多个不同的事物,然后通过“重载解析”解决歧义。方法和算子重载决策算法的具体细节不同,但它们在整体算法中是相似的:首先确定一个候选集,然后删除不适用的候选集,然后一个更好的算法消除比另一个更差的适用候选集,然后是bestness 算法确定剩下的唯一最佳候选者(如果有)。
(3)您不能限制运算符的访问修饰符,它们必须是公共的,考虑到第 1 点,这没有意义。但考虑到第 2 点,有点意义。
我根本不明白第 (3) 点与第 (1) 或 (2) 点有什么关系。运算符必须是公共表面区域的一部分的限制是为了防止当您在课堂内时能够将 a 添加Fruit
到 an而不是在课堂内时添加 a 的混乱情况。Animal
Apple
Giraffe
运算符在类或结构中声明,因此“属于”所述类型,它们不会浮动“属于”没有给定类型。那么当我在一个类中声明一个运算符时,我究竟重载了什么?
您正在使运算符超载。
int 之间存在相同的运算符并不意味着我正在重载任何属于 int 的运算符。对我来说,这就像说Foo.Hello()
andBar.Hello(string hello)
是 的重载一样Hello
。它们不是以两种不同的类型声明的。与运营商有什么区别?
您刚刚准确地描述了差异。方法重载和操作重载在许多细节上有所不同。
如果你想采取Foo.Hello()
和Bar.Hello(string)
的“重载”的立场Hello
,这不是一个常见的立场,但它在逻辑上是一致的。
我的印象是重载时无法更改访问修饰符。
你的印象是错误的;覆盖虚拟方法时不能更改访问修饰符。您已经将其与重载混淆了。
(我注意到有一种情况需要您在覆盖虚拟方法时更改访问修饰符;您能推断出它是什么吗?)
我也有这样的印象,如果没有至少一个操作数是您声明运算符的类型,就不能声明运算符。
这几乎是正确的。用户定义的运算符必须具有 type 的操作数T
,其中T
是封闭类或结构类型,或者 T?
ifT
是结构类型。
那么第三类如何能够访问给定的运算符,而另一个第三类却不能,除非一个属于外部程序集而另一个不属于外部程序集,在这种情况下,我根本不觉得它令人困惑甚至有用?
你错误地描述了我的例子,这本来可以更清楚。这是非法的:
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!
}
这只是一个例子。一般来说,使运算符的重载解析依赖于可访问域是一件奇怪的事情,因此语言设计者通过要求用户定义的运算符是公共的来确保这种情况永远不会发生。
我只是在运算符的上下文中发现重载令人困惑。
很多人都这样做,包括编译器编写者。规范中用户定义的操作符部分极难解析,而且微软的实现是编译器错误的丰富来源,其中很多都是我的错。
我不明白为什么在类型中简单地声明运算符必须与描述声明任何其他静态方法的方式不同。
嗯,不同的东西是不同的;运算符在很多方面都不同于方法,包括它们的重载解析算法。
我从来没有特别喜欢 C# 具有可重载的运算符。C# 功能是 C++ 中相同功能的更好设计版本,但在两种语言中,我认为该功能所需的成本远高于相应的用户收益。
谢天谢地,至少 C# 并没有<<
像惯用的 C++ 那样彻底滥用运算符——尽管它当然会滥用+
和-
代表委托。