最近逛CSDN发现个问题
int i = 0;
i = i++;
Consloe.Write(i);
发现输出为0的情况,有各种各样的解释
比如说,后缀++是先取值赋值给i,再进行i=i+1的操作。
我觉得这不是个合理的解释,先取值赋值给i,那i这时候等于0,在进行i=i+1的操作,i就应该等于1了。
无论等号左边右边怎样,变量i在内存中的位置都应该是同一个,所以就算是先取值再自增,那自增的都会是i。
所以我觉得,先取值再自增的解释并不合理,或者说后缀++是先取值再自增的这个说法就是错误的。
但去翻了各种贴子资料,都没有直接抨击这个说法,也去翻了C#文档也没有提及++的底层实现(可能有,只是我没找到)
那只好自己动手了。
如何证明呢,从运算符优先级和运算符重载上我找到了思路。
1.运算符优先级:++的优先级要高于=的优先级,所以在进行=操作时,++运算符必须得运算完毕,不会存在任何延时操作,也就是++的运算符不能分两步走,不能一步取值在=号前,一步自增在=后。
所以我觉得取值和自增都得在一个“时间“完成。
这一个时间是多久呢,从++运算符重载就能知道,是在一个函数(方法)内
2.++运算符重载:如果让你在一个方法内实现先取值再自增,你会怎么做呢?
开启一个新线程挂起,然后等取值操作执行完,这是很不合理的。
合理的方案是,用一个temp保存自增前的值,然后自增,最后返回temp。
再来看 i=i++ , 用temp保存原来的值temp=0,i自增i=1,返回temp,即i=temp=0。i有自增,但被temp覆盖,所以i值不变。
这很合理不是吗,怎么验证一下呢。(可以反编译之类的高端操作,无奈我不会)
在C++中自增操作符重载分前缀重载和后缀重载,你要在一个方法内前缀重载,完成先取值再自增,我认为这完全不用验证了,这就是正确的,也没办法验证。(找了好久都找不到底层int++的代码)
在C#中自增操作符的重载不分前后缀,是底层帮我们实现了前缀和后缀的区别
public struct Age //注意是结构体
{
public int Value;
public Age(int value)
{
Value = value;
}
//前后缀++都是同一种重载方式,调用时底层自动区分前后缀实现
public static Age operator ++(Age age)
{
age.Value += 1;
return age;
}
}
class Program
{
static void Main(string[] args)
{
var myAge = new Age(18);
Console.WriteLine("myAge.value = " + myAge.Value);
myAge = myAge++;
Console.WriteLine("myAge.value = " + myAge.Value);
Console.ReadKey();
}
}
输出
当把struct改成class时
public class Age //注意是类
{
……
}
输出,如果是引用类型,就不存在前后缀的区别
可以推测底层是这样的
后缀++(Age age)
{
var temp = age;
age++(); //调用age++的方法
return temp;
}
所以值类型的temp不受影响,而引用类型temp会随原来的age改变,也就是引用类型的自增不分前后缀,都是返回自增后的值
总结:后缀++,先取值再自增,是对方法内的说法。对方法外应该是,进行自增但返回自增前的值。
(不是很严谨,但我觉得足够了,三五年后我可能能找到更直接的证明方法。)