如何准确地解释+=和-=操作符
本文关键字:操作符 何准确 解释 | 更新日期: 2023-09-27 18:16:59
究竟(在引擎盖下)+=
和-=
操作符做什么?
或者它们是隐式的,因为它们是每个类型定义的?
我已经广泛地使用了它们,这是语法的一个非常简单的特性,但是我从来没有想过它是如何工作的。
什么带来了这个问题
我可以像这样连接一个字符串值:
var myString = "hello ";
myString += "world";
所有罚款。但为什么这对集合不起作用呢?
var myCol = new List<string>();
myCol += "hi";
你可能会说'你想要添加一个不同的类型,你不能将字符串添加到一个非字符串的类型'。但是下面的代码也不行:
var myCol = new List<string>();
myCol += new List<string>() { "hi" };
好吧,也许它不与集合工作,但下面不是事件处理程序的(一种)集合?
myButton.Click += myButton_Click;
我显然对这些操作符的工作原理缺乏深入的理解。
请注意:我不是试图建立收集myCol
在这种方式,在一个真正的项目。我只是好奇这个算子的工作原理,这是假设的。
+=
运算符的隐式定义如下:a += b
变为a = a + b;
,与-=
运算符相同。
(注意:正如Jeppe指出的,如果a
是一个表达式,如果使用a+=b
,它只计算一次,但使用a=a+b
则计算两次)
不能分别重载+=
和-=
操作符。任何支持+
运算符的类型也支持+=
。您可以通过重载+
和-
来为自己的类型添加对+=
和-=
的支持。
事件具有
+=
和-=
操作符,用于向订阅的事件处理程序列表中添加和删除事件处理程序。尽管如此,它们不支持+
和-
操作符。这不是您可以通过常规操作符重载为自己的类做的事情。
正如另一个答案所说,+
运算符没有为List<>
定义。你可以检查它试图重载它,编译器会抛出这个错误One of the parameters of a binary operator must be the containing type
。
但是作为实验,您可以定义继承List<string>
的自己的类并定义+
操作符。像这样:
class StringList : List<string>
{
public static StringList operator +(StringList lhs, StringList rhs)
{
lhs.AddRange(rhs.ToArray<string>());
return lhs;
}
}
那么你可以这样做,没有问题:
StringList listString = new StringList() { "a", "b", "c" };
StringList listString2 = new StringList() { "d", "e", "f" };
listString += listString2;
编辑
根据@EugeneRyabtsev注释,我对+
操作符的实现将导致意外行为。所以应该更像这样:
public static StringList operator +(StringList lhs, StringList rhs)
{
StringList newList=new StringList();
newList.AddRange(lhs.ToArray<string>());
newList.AddRange(rhs.ToArray<string>());
return newList;
}
简短的回答是c#中的操作符必须为给定的类型重载。这也适用于+=
。string
包含该操作符的重载,但List<>
没有。因此,对列表使用+=
运算符是不可能的。对于委托,+=
操作符也是重载的,这就是为什么您可以在事件处理程序上使用+=
。
稍微长一点的答案是,您可以自己自由地重载操作符。但是,您必须为自己的类型重载它,因此为List<T>
创建重载是不可能的,而实际上您可以为自己的类(例如继承自List<T>
的类)这样做。
从技术上讲,您实际上并没有重载+=
-操作符,而是重载+
操作符。然后通过将+
操作符与赋值结合来推断+=
操作符。为了使其工作,+
操作符应该以这样一种方式重载,即结果类型与第一个参数的类型匹配,否则当您尝试使用+=
时,c#编译器将抛出错误消息。
正确的实现实际上比人们想象的要复杂得多。首先,说a += b
和a = a+b
完全相同是不够的。在最简单的情况下,它具有相同的语义,但它不是一个简单的文本替换。
首先,如果左边的表达式比一个简单的变量更复杂,它只求值一次。所以M().a += b
和M().a = M().a + b
是不一样的,因为那样会在一个完全不同的对象上赋值,或者会导致方法的副作用发生两次。
如果M()
返回引用类型,则复合赋值操作符可以被认为是var obj = M(); obj.a = obj.a+b;
(但仍然是表达式)。然而,如果obj
是一个值类型,这种简化也将不起作用,如果方法返回一个引用(c# 7中新增),或者如果它实际上是一个数组元素,或者从索引器返回的东西等等,那么操作符将确保它不会复制多于修改对象所需的副本,并且它将被应用到正确的位置,没有额外的副作用。
+=
导致调用add访问器,-=
导致调用事件的remove访问器。如果这些访问器不是用户实现的,则事件赋值可能导致调用Delegate。组合和Delegate。移除类范围内内部委托对象上的。这也是为什么不能简单地从类外部获取事件对象的原因,因为它不是公共的。在本例中,+=
/-=
也不是表达式。
我推荐阅读Eric Lippert的复合式作业。