InB*_*een 6 c# operator-overloading
随着我在我的小数学库上的进步,我绊倒了C#和.NET Framework的某些方面,我在理解方面遇到了一些麻烦.
这次它是运算符重载,特别是术语重载本身.为什么称为重载?默认情况下,所有对象都具有所有运算符的实现吗?那就是:
public static object operator +(object o1, object o2)
Run Code Online (Sandbox Code Playgroud)
在某处预定义并以某种方式?如果是这样,为什么然后如果我尝试o1 + o2我得到编译时错误Operator '+' cannot be applied to operands of type 'object' and 'object'?这将以某种方式暗示默认情况下对象没有预定义的运算符,那么术语重载怎么样呢?
我问这个,因为在我的数学库中,使用3D几何元素,我有以下结构:Vector和Point.现在,在内部我想允许以下构造来创建向量:
static Vector operator -(Point p1, Point p2) {...}
Run Code Online (Sandbox Code Playgroud)
由于这在数学上不是100%正确但在内部非常有用以减少代码混乱,我不想公开公开此运算符,所以我的初衷是简单地做:
internal static Vector operator -(Point p1, Point p2) {...}
Run Code Online (Sandbox Code Playgroud)
令人惊讶的是(对我来说)我得到了以下编译时错误:User-defined operator 'Geometry.operator +(Geometry.Vector, Geometry.Vector)' must be declared static and public".
现在这一限制,所有运营商必须是公开的并似乎使某种意义上与整个超载运营商方面,但它似乎是所有类型的不一致:
object + object默认情况下我不能这样做,或者事先myTpye + myType没有显式定义+运算符.operator,它们必须是public,考虑到第1点,这是没有意义的.但考虑到第2点,有点理解.有人可以用简单的术语解释我所做的这些混乱吗?
Eri*_*ert 16
为什么称为重载?
它被称为"重载",因为它正在超载.当我们为这个东西提供两个可能的实现然后必须决定使用哪个(称为重载解析)时,我们"重载"一个东西.
当我们重载方法时,我们给出两个或多个具有给定名称的方法的实现.当我们重载运算符时,我们为具有给定语法的运算符提供两个或更多可能的实现.这是同一件事.
确保您不会因重写而混淆重载.重载只是在同一个声明空间中存在两个具有相同名称/语法的方法/运算符.覆盖处理在运行时如何填充虚拟方法槽的内容.
默认情况下,所有对象都具有所有运算符的实现吗?
没有.
在
public static object operator +(object o1, object o2)某处预定义并以某种方式?
没有.
这将以某种方式暗示默认情况下对象没有预定义的运算符,那么术语重载怎么样呢?
我不明白这个问题.当然C#有预定义的运算符.
我不想公开公开这个运营商
然后不要使用运算符; 制作私人,内部或受保护的方法.
C#的设计是操作员始终是一种类型的公共表面区域的一部分.让操作符具有取决于使用的可访问域的含义是非常令人困惑的.C#经过精心设计,成为一种"质量陷阱"语言,语言设计者的选择使您远离写作混乱,越野车,难以重构的程序.要求用户定义的运算符是公共的和静态的是那些微妙的设计点之一.
(1)默认情况下,对象没有预定义的运算符
当然可以; 在各种对象上有数百个预定义的运算符.例如,对于添加,运算符有以下预定义重载+:
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)
Run Code Online (Sandbox Code Playgroud)
有关所有其他运算符的所有预定义重载的列表,请参阅C#规范.
请注意,运算符的重载决策有两个阶段:首先,重载决策尝试查找唯一最佳的用户定义的重载; 只有这样做才发现没有适用的候选者是重载决议考虑的预定义重载.
(2)定义运算符不会被描述为创建运算符,它被描述为重载,它与点1在某种程度上是不一致的(对我而言).
我不明白为什么你发现它不一致,或者,就此而言,你发现不一致的东西.术语"过载"一直用于描述操作符和方法; 在这两种情况下,它意味着使用相同的语法来引用两个或更多不同的东西,然后通过"重载分辨率"解决歧义.方法和运算符重载决策算法的确切细节是不同的,但它们在整个算法中是相似的:首先识别候选集,然后删除不适用的候选者,然后更好的算法消除比另一个更差的适用候选者,然后a最佳性算法确定剩下的唯一最佳候选者(如果有的话).
(3)你不能限制运营商的访问修改器,它们必须是公共的,考虑到第1点这没有意义.但考虑到第2点,有点有意义.
我不明白第(3)点与点(1)或(2)有什么关系.运营商必须是公共面积的一部分的限制是为了防止能够添加一个混乱的局面Fruit,以一Animal当你在里面类Apple,但不是在你的内部类Giraffe.
运算符在类或结构中声明,因此"属于"所述类型,它们不会"归属"到任何给定类型.那么当我在一个类中声明一个运算符时,我的重载是什么?
你的运营商正在超载.
在int之间存在相同的运算符并不意味着我正在重载任何东西,因为该运算符属于int.对我来说,就像说
Foo.Hello()和Bar.Hello(string hello)过载一样Hello.它们不是以两种不同的类型声明它们.与运营商有什么区别?
你刚才准确地描述了这种差异.方法重载和操作重载的许多细节都不同.
如果你想采取的立场是Foo.Hello()和Bar.Hello(string)是"重载" Hello,这不是一个共同立场,采取,但它在逻辑上是一致的.
我的印象是你在重载时无法更改访问修饰符.
你的印象是错的; 覆盖虚拟方法时,无法更改访问修饰符.你把它与重载混淆了.
(我注意到有一种情况需要在覆盖虚拟方法时更改访问修饰符;你能推断出它是什么吗?)
我还认为,如果没有至少一个操作数属于您声明操作符的类型,则无法声明操作符.
这几乎是正确的.用户定义的操作者必须有类型的操作数T,其中,T是封闭类或结构类型,或者 T?如果T是结构类型.
那么第三类怎么能访问一个给定的运算符,而另一个第三类不能除非一个属于外部程序集而另一个不在这种情况下我根本不会发现它令人困惑甚至有用呢?
你错误地描述了我的例子,这可能更清楚了.这是非法的:
public class Fruit
{
protected static Shape operator +(Fruit f, Animal a) { ... }
}
Run Code Online (Sandbox Code Playgroud)
因为这很奇怪:
public class Apple : Fruit
{
...
Shape shape = this + giraffe; // Legal!
}
public class Giraffe : Animal
{
...
Shape shape = apple + this; // Illegal!
}
Run Code Online (Sandbox Code Playgroud)
这只是一个例子.一般来说,做一个奇怪的事情是,使运算符的重载决策取决于可访问性域,因此语言设计者确保通过要求用户定义的运算符公开来实现这一点.
我只是在运营商的背景下发现过载令人困惑.
很多人都这样做,包括编译器编写者.规范的用户定义的运算符部分非常难以解析,并且Microsoft实现是编译器错误的丰富来源,其中许多是我的错.
我不明白为什么简单地声明类型中的运算符必须与描述声明任何其他静态方法的方式不同.
嗯,不同的东西是不同的; 运算符在很多方面都与方法不同,包括它们的重载决策算法.
我从来没有特别喜欢C#具有可重载的运算符.C#功能是C++中相同功能的设计稍好的版本,但在这两种语言中,我认为该功能的成本远远高于相应的用户利益.
谢天谢地,至少C#没有<<像惯用语C++那样彻底滥用运算符 - 尽管它当然会滥用+和-代表.