List中的值类型无法修改的原因详解
2014-12-09 21:20
295 查看
http://blog.csdn.net/wuyazhe/article/details/5676881
[c-sharp] view
plaincopy
public struct AA
{
public int value;
public AA(int v)
{
value = v;
}
}
public class Test
{
public void Run()
{
List<AA> datas = new List<AA>();
datas.Add(new AA(1));
datas.Add(new AA(2));
}
}
我们假设有这样的一个结构体。因为结构体是值类型的,在没有修饰的情况下,我们的方法中,传入,传出都是传递的值,每次传递都进行了一次值的拷贝。
所以,我们这样操作是不可行的。
datas[1].value = 10;
为什么呢,因为datas[1]不是第二个对象,而是第二个对象的副本,你修改副本,当然不会影响原本的值了。正确的写法是这样的
datas[1] = new AA(10);
这只是开始。可能有人会问,为什么我用data[i]是这个元素的副本呢?我们来详细的介绍一下。
首先,请先阅读MSDN以增加一些基础的了解
struct(C# 参考)
结构(C# 编程指南)
如何:了解向方法传递结构和向方法传递类引用之间的区别(C#
编程指南)
了解过基础,我们看看正题
[c-sharp] view
plaincopy
void Foo(StructValue o){}
//没有人怀疑这里的o是个结构体对象的副本。对吧。这个还有疑问的复习msdn去。
StructValue Foo()
{
StructValue f;
return f;//你认为这里返回的是f还是f的副本呢?显然也是副本。
}
这里都明白,返回的f是定义的f的一个副本,没有问题吧。
看看List<T>的索引器
[c-sharp] view
plaincopy
public T this[int index]
{
[TargetedPatchingOptOut("Performance critical to inline across NGen image boundaries")]
get
{
if (index >= this._size)
{
ThrowHelper.ThrowArgumentOutOfRangeException();
}
return this._items[index];//这里返回的是this._items[index]的副本,能理解了吧。
}
[TargetedPatchingOptOut("Performance critical to inline across NGen image boundaries")]
set
{
if (index >= this._size)
{
ThrowHelper.ThrowArgumentOutOfRangeException();
}
this._items[index] = value;//这里是把你的value副本复制到索引器指定的位置
this._version++;
}
}
以上3个例子看懂。这个问题就清晰了。
贴的这3个代码已经很明显的告诉你这样一个原因:
data[1]是通过List<T>的索引器访问的值类型数组中的某个元素,返回的是这个元素的副本。而设置也是副本设置,所以可以设置,可以读取,不可以通过副本的修改影响到List<T>中的值类型数组。
为了进一步证明这一点,我来用一个反射的例子来演示一下:
[c-sharp] view
plaincopy
private static void TestChangeStructList()
{
//定义一个泛型List
List<AA> datas = new List<AA>();
//添加2个元素,value分别为1,2
datas.Add(new AA(1));
datas.Add(new AA(2));
//打印出当前元素
Console.WriteLine("原始List:");
datas.ForEach(o => Console.WriteLine(o.value.ToString()));
//使用List的索引器赋值
datas.ForEach(o => o.value++);
//打印出当前元素
Console.WriteLine("使用List的索引器赋值");
datas.ForEach(o => Console.WriteLine(o.value.ToString()));
//反射List内部的值类型数组赋值
AA[] items = (AA[])((FieldInfo)(datas.GetType().GetMember("_items", BindingFlags.NonPublic | BindingFlags.Instance)[0])).GetValue(datas);
items[0].value++;
items[1].value++;
//打印出当前元素
Console.WriteLine("反射List内部的值类型数组赋值");
datas.ForEach(o => Console.WriteLine(o.value.ToString()));
}
结果:
原始List:
1
2
使用List的索引器赋值
1
2
反射List内部的值类型数组赋值
2
3
还没看懂的,最后再讲一次。
List<T>[i]
这是叫做索引器的,索引器是一种属性,属性就是在调用方法,而值类型无法返回一个引用,返回的是值,所以索引器返回的,是你添加进去变量的副本。而因为值类型无法传递引用,所以添加实际也是使用副本的方式添加的。所以对于值类型的List<T>,索引器的结果,可以访问,可以修改,但无法直接存回去,如何保存?可以重新的赋值,例如
List<Point> points = new List<Point>();
points.Add(new Point());//0,0
修改呢,就整个重新复制
points[0] = new Point(1,1);
你不能修改一项points[0].X = 1;
这样不可以的。
希望这样说,各位不明白的能明白。明白的更明白。
[c-sharp] view
plaincopy
public struct AA
{
public int value;
public AA(int v)
{
value = v;
}
}
public class Test
{
public void Run()
{
List<AA> datas = new List<AA>();
datas.Add(new AA(1));
datas.Add(new AA(2));
}
}
我们假设有这样的一个结构体。因为结构体是值类型的,在没有修饰的情况下,我们的方法中,传入,传出都是传递的值,每次传递都进行了一次值的拷贝。
所以,我们这样操作是不可行的。
datas[1].value = 10;
为什么呢,因为datas[1]不是第二个对象,而是第二个对象的副本,你修改副本,当然不会影响原本的值了。正确的写法是这样的
datas[1] = new AA(10);
这只是开始。可能有人会问,为什么我用data[i]是这个元素的副本呢?我们来详细的介绍一下。
首先,请先阅读MSDN以增加一些基础的了解
struct(C# 参考)
结构(C# 编程指南)
如何:了解向方法传递结构和向方法传递类引用之间的区别(C#
编程指南)
了解过基础,我们看看正题
[c-sharp] view
plaincopy
void Foo(StructValue o){}
//没有人怀疑这里的o是个结构体对象的副本。对吧。这个还有疑问的复习msdn去。
StructValue Foo()
{
StructValue f;
return f;//你认为这里返回的是f还是f的副本呢?显然也是副本。
}
这里都明白,返回的f是定义的f的一个副本,没有问题吧。
看看List<T>的索引器
[c-sharp] view
plaincopy
public T this[int index]
{
[TargetedPatchingOptOut("Performance critical to inline across NGen image boundaries")]
get
{
if (index >= this._size)
{
ThrowHelper.ThrowArgumentOutOfRangeException();
}
return this._items[index];//这里返回的是this._items[index]的副本,能理解了吧。
}
[TargetedPatchingOptOut("Performance critical to inline across NGen image boundaries")]
set
{
if (index >= this._size)
{
ThrowHelper.ThrowArgumentOutOfRangeException();
}
this._items[index] = value;//这里是把你的value副本复制到索引器指定的位置
this._version++;
}
}
以上3个例子看懂。这个问题就清晰了。
贴的这3个代码已经很明显的告诉你这样一个原因:
data[1]是通过List<T>的索引器访问的值类型数组中的某个元素,返回的是这个元素的副本。而设置也是副本设置,所以可以设置,可以读取,不可以通过副本的修改影响到List<T>中的值类型数组。
为了进一步证明这一点,我来用一个反射的例子来演示一下:
[c-sharp] view
plaincopy
private static void TestChangeStructList()
{
//定义一个泛型List
List<AA> datas = new List<AA>();
//添加2个元素,value分别为1,2
datas.Add(new AA(1));
datas.Add(new AA(2));
//打印出当前元素
Console.WriteLine("原始List:");
datas.ForEach(o => Console.WriteLine(o.value.ToString()));
//使用List的索引器赋值
datas.ForEach(o => o.value++);
//打印出当前元素
Console.WriteLine("使用List的索引器赋值");
datas.ForEach(o => Console.WriteLine(o.value.ToString()));
//反射List内部的值类型数组赋值
AA[] items = (AA[])((FieldInfo)(datas.GetType().GetMember("_items", BindingFlags.NonPublic | BindingFlags.Instance)[0])).GetValue(datas);
items[0].value++;
items[1].value++;
//打印出当前元素
Console.WriteLine("反射List内部的值类型数组赋值");
datas.ForEach(o => Console.WriteLine(o.value.ToString()));
}
结果:
原始List:
1
2
使用List的索引器赋值
1
2
反射List内部的值类型数组赋值
2
3
还没看懂的,最后再讲一次。
List<T>[i]
这是叫做索引器的,索引器是一种属性,属性就是在调用方法,而值类型无法返回一个引用,返回的是值,所以索引器返回的,是你添加进去变量的副本。而因为值类型无法传递引用,所以添加实际也是使用副本的方式添加的。所以对于值类型的List<T>,索引器的结果,可以访问,可以修改,但无法直接存回去,如何保存?可以重新的赋值,例如
List<Point> points = new List<Point>();
points.Add(new Point());//0,0
修改呢,就整个重新复制
points[0] = new Point(1,1);
你不能修改一项points[0].X = 1;
这样不可以的。
希望这样说,各位不明白的能明白。明白的更明白。
相关文章推荐
- List中的值类型无法修改的原因详解
- 无法将类型为“*Class”的 COM 对象强制转换为接口类型 失败原因.
- 我的C# 中无法修改字体的大小 是什么原因 该如何解决
- 详解win7中的文件属性“访问时间”和“修改时间”不一致的原因分析 【原来LastAccessTime早就没用了】
- MVC 无法将类型“System.Collections.Generic.List<AnonymousType#1>”隐式转换为“System.Collections.Generic.IList<Mvc3Modeltest.Models.Movie>”。存在一个显式转换(是否缺少强制转换?))
- 解决无法在unicode和非unicode字符串数据类型之间转换的方法详解
- Intersge无法启动--原因是Message Queues的资源不足,共享内存不足 RedHat如何在系统运行过程中修改内核参数
- 使用ajax返回json类型数据的时候无法进入success可能的原因
- SQL 无法装载DLL Microsoft原因(无法修改sa密码)
- VC6.0无法修改程序 原因 今天遇到了
- 为什么我上传了flv或MP4文件到服务器,可输入正确地址通过http协议来访问总是出现“无法找到该页”的404错误呢?这就表明mp4格式文件是服务器无法识别的,其实,这是没有在iis中将相应的MIME类型进行设置的原因。那该怎样设置MIME
- 强类型的数据集,在处理以直接附加方式的SQLServer的MDF文件时无法更新数据-原因分析
- 无法分析从服务器收到的消息。之所以出现此错误,常见的原因是: 在通过调用 Response.Write() 修改响应时,将启用响应筛选器、HttpModule 或服务器跟踪。
- asp(非asp.net)程序访问access数据库,无法写入、修改,提示错误:当前记录集不支持更新。这可能是提供程序的限制,也可能是选定锁定类型的限制。
- 如何修改DeDe标签Pagelist分页样式详解
- 无法分析从服务器收到的消息。之所以出现此错误,常见的原因是: 在通过调用 Response.Write() 修改响应时,将启用响应筛选器、HttpModule 或服务器跟踪。
- 错误 1 无法将带 [] 的索引应用于“StrigDs.SeqList”类型的表达式 C:/Inetpub/wwwroot/StrigDs/StrigDs/Form5.cs 39 13 StrigDs
- 无法分析从服务器收到的消息。之所以出现此错误,常见的原因是: 在通过调用 Response.Write() 修改响
- 修改List报ConcurrentModificationException异常原因分析
- mysql中修改表字段名/字段长度/字段类型详解