记得之前写过一篇有关字典踩坑的文章。
实际上还是有关值类型的事情。这里再扩展三个相关的小问题,作为一个笔记记录下来。
//辅助结构体。
public struct Value
{
public int value;
public void Add(int v)
{
value += v;
}
}
//装箱拆箱
public static void Q1()
{
Value v = new Value();
void Method(ref object obj)
{
Value result = (Value)obj;
result.Add(1);
obj = result;
}
object obj = v;
Method(ref obj);
v.value.Log();//输出什么?
}
//闭包
public static void Q2()
{
Value sum = new Value();
Action act = null;
for (int i = 0; i < 3; i++)
{
act += () => sum.Add(++i);
}
act.Invoke();
sum.value.Log();//输出什么?
}
//数组和List
public static void Q3()
{
var v = new Value();
Value[] array = new Value[] { v };
array[0].Add(1);
array[0].value.Log();//输出什么?
List<Value> list = new List<Value>() { v };
list[0].Add(1);
list[0].value.Log();//输出什么?
v.value.Log();//输出什么?
}
这里分别解释一下。
Q1是个有关装箱拆箱的问题。在装箱的时候,会将栈上的值复制到堆上,所以object obj = v这一步进行了拷贝,操作obj并不会影响原来的v;同样地,在拆箱的时候也会从堆上拷贝到栈上,所以Method内部,假设直接调用((Value)obj).Add(1)也不会影响obj。最后输出的就是0。
这里没有展开Method方法,是营造一种使用场景的氛围,表示这里装箱拆箱是为了传递方法参数。这个问题也表明了ref object并不能避免值类型的拷贝,因为在装箱拆箱的过程中就已经进行了拷贝。
Q2是有关闭包的问题。委托中以引用的形式捕获了外部的变量i,所以在Invoke执行时,每次的i获取的都是引用值。在For循环结束后i是3,每次++i又增加了i值。所以输出值为4+5+6=15。如果写一个临时变量int temp=++i,那么就会生成3个不同的临时变量被闭包捕获,输出值就是1+2+3=6了。
有次别人问我一个类似的问题,在循环中为十个按钮分别注册输出事件,说是有过一定实际开发经历的人才能做对。我也是经常忘记一些特性,然后再验证,再忘记这样。好在现在一些知识性的内容可以直接问AI,节省了不少时间。
Q3是一个涉及数组和List的问题。老实说我一开始都不知道是这样的输出结果,把问题抛给DeepSeek-0528一样没回答对。三个输出分别是1,0,0。这似乎涉及到数组的访问细节:尽管这里并没有使用ref array[0]的访问方法,但访问到的仍然是引用;而list[0]访问到的是值的拷贝,所以并没有作用。此外,这里数组可以直接使用array[0].value=1这样的赋值方式,而list则不能。
尽管使用值类型可能在各种意想不到的地方出问题,但用好了也确实方便高效。
文章版权归作者所有





- 最新
- 最热
只看作者