关于 C# 自增运算符(operator ++)的重载
来源:互联网 发布:淘宝关键词采集器 编辑:程序博客网 时间:2024/05/21 19:42
C# 支持用户自定义 struct
和 class
的运算符重载。但是对于自增运算符(operator ++
),有一些细节需要留意。特别是如果按照 C++ 自增运算符重载的思路去理解 C# 的自增运算符重载,那会遇到很多问题。
重点提示
1. 不要在 class
上定义自增运算。
2. 在 struct
上定义自增运算函数(operator++
)时,必须返回一个比原值大 1 的新 struct
。
引例
用一个例子引入。比如我们定义如下的一个简单的 struct
,来 wrap 一个整数(这样做的目的有多种可能,比如在算术溢出时抛出异常;但这不是本文的重点):
struct MyInteger{ public int Value { get; private set; } public MyInteger(int value) { this.Value = value; }}
如果我们希望像使用寻常 int
变量那样使用 MyInteger
,比如像下面这样使用:
for (MyInteger i = 0; i < 5; i++){ // ...}
那该怎么办呢?自然,我们需要重载 <
运算符(及 >
运算符)和 ++
运算符。本文只探讨如何重载 ++
运算符的问题。
先看看一种“自然”但错误的方式(这种方式和 C++ 里重载后置++
运算符的形式非常相似):
// The following code will NOT work as expected.public static MyInteger operator++(MyInteger x){ MyInteger y = x; x.Value++; return y;}
这段代码实际上是行不通的。比如在前一段循环中,i
永远都是零。
分析
要理解其原因,必须看看 C# 是如何处理 ++
重载的。C# 里的 ++
运算符重载方式与 C++ 不同:在 C++ 中,前置++
和后置++
通过不同的函数重载,其行为完全由实现者控制,并且也由实现者来保证其行为符合常规的前置和后置语义。而在 C# 中,++
运算符的重载函数只有一个,这个函数应该做的事情就是返回一个比参数大 1 的值;而前置和后置的语义是由编译器决定的,实现者自己没法修改。
说了这么多废话,其实需要知道的只是下面两条规则:
1. C# 的前置自增(j = ++i
)等价于
i = operator++(i);j = i;
2. C# 的后置自增(j = i++
)等价于
j = i;i = operator++(i);
这两条规则对 struct
和 class
都适用。
尽管看上去很简单,但是这与 C++ 中的自增运算有微妙的区别。可以这么说,在 C# 里不存在“自”增这回事;看上去的“自”增,实际上是令被自增的变量等于一个以该变量为参数的函数的值。
进一步,由于 struct
和 class
的特性,前置和后置 ++
的特性也不相同:
对于 struct
1. 为了实现前置自增(j=++i
)语义,operator++
函数必须返回 i+1
的值;但其内部对i
的修改并不能产生任何效果(因为struct
按值传递)。C# 会保证 j
等于自增后的i
值。
2. 为了实现后置自增(j=i++
)语义,operator++
函数必须返回 i+1
的值;但其内部对i
的修改并不能产生任何效果(因为struct
按值传递)。C# 会保证 j
等于自增前的i
值。
对于 class
1. 为了实现前置自增(j=++i
)语义,operator++
函数必须返回等价于 i+1
的对象;这个对象可以是新对象,也可以是自增之后的i
本身;而j
永远和 i
指向同一个对象。
2. 为了实现后置自增(j=i++
)语义,operator++
函数必须返回等价于 i+1
的新对象,因为j
已经指向了i
自增前的那个对象,如果 operator++
仅仅是修改了 i
的值就直接返回 i
的话,那么 j
的值也会变成了自增以后的值了。
总结
综合上述四种情况可以发现,唯一放之四海而皆准的 operator++
实现方法,就是返回一个新的值(对于 struct
)或者对象(对于 class
)。
比如,在最初的例子当中,正确的(或者说恰当的)operator++
的定义方式是这样的:
// This is the right way to implement ++ on a struct.public static MyInteger operator++(MyInteger x){ return new MyInteger(x.Value + 1);}
然而,对于类上定义的自增运算,还有一个问题。考虑下面的代码:
// i, j are objects of the same class.j = i;++j;在
++j
之后,j
的值自然是 +1 了,那么 i
的值应该不变还是 +1 呢?答案并不显然。如果operator++
函数返回了一个新对象,那么i
的值不变;如果 operator++
返回了修改后的对象,那么i
的值会跟着变。而从直观上讲,++j
应当增加j
指向的对象呢,还是让 j
另外指向一个比原来大 1 的对象呢?这也没有很自然的答案。因此,最好遵循下面这条规则:不要在 class
上定义自增运算。而只在 struct
上定义自增运算。
- 关于 C# 自增运算符(operator ++)的重载
- 运算符的重载 operator
- 关于运算符"operator<<"重载出错解决方法
- operator重载运算符
- operator 运算符重载
- operator运算符重载
- operator重载运算符
- 运算符重载 编程题#3(专项课程3;重载:* operator, & operator=, & operator(); 函数 memcpy 的使用)
- operator运算符的重载问题
- operator重载运算符(一)
- 运算符重载(operator overloading)
- 自增运算符的重载
- 自增++运算符的重载
- 关于operator运算符重载,简单练习说明原理
- * 运算符重载 编程题#4:大整数的加减乘除(Coursera 程序设计与算法;重载:& operator <<, & operator >>, +, -, *, /)
- C++运算符重载(operator)
- C++重载运算符operator
- 运算符重载之operator
- 画线函数Glib_Line算法的研究
- 对象锁死锁导致程序自动关闭
- 进度栏编程
- OpenCV学习笔记(五)—内存存储
- Time类
- 关于 C# 自增运算符(operator ++)的重载
- 学习效果要求和作业要求
- weblogic
- Android应用程序的生命周期
- xcode编译Warning: Attempt to present xx on yy whose view is not in the window hierarchy!
- MyEclipse上安装SVN插件
- 重新组织数据之六 :Duplicate Observed Data(复制「被监视数据」)
- sql server 2008 不允许保存更改,您所做的更改要求删除并重新创建以下表 的解决办法
- Java乔晓松-spring自动装配Bean的4种方式