有关值类型和引用类型的问题

VRC289 2010-06-17 05:07:43

class Program
{
static void Main(string[] args)
{
List<Item> items = new List<Item>();

//添加
Item item1 = new Item();
item1.Id = 0;
item1.DisplayText = "test1";
items.Add(item1);

//添加
Item item2 = new Item();
item2.Id = 1;
item2.DisplayText = "test2";
items.Add(item2);

//Item item = items[1];
//item.DisplayText = "金星";
//items[1] = item;
items[0].DisplayText = "test3"; // 这个地方会报错误

}

public struct Item
{
public int Id;
public string DisplayText;
}
}



items[0] 是一个结构体,items[0].DisplayText 是给这个结构体赋值 为什么这个地方不行了

我知道结构体是值类型 类是引用类型

有人解释:“结构类型是值类型,这是C#编译器的设计,所以不能像类一样用.号引用成员!” 这是什么意思


...全文
209 33 打赏 收藏 转发到动态 举报
写回复
用AI写文章
33 条回复
切换为时间正序
请发表友善的回复…
发表回复
兔子-顾问 2010-06-18
  • 打赏
  • 举报
回复
忘记贴了,AA定义

public struct AA
{
public int value;
public AA(int v)
{
value = v;
}
}
兔子-顾问 2010-06-18
  • 打赏
  • 举报
回复
处于好奇好玩,写个例子给大家看看。以证明我上面的分析。

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;
这样不可以的。

希望这样说,各位不明白的能明白。明白的更明白。
wolf_xjf 2010-06-18
  • 打赏
  • 举报
回复
综合大家的观点,我结合jbo126的观点,谈点个人理解,list中真正装载的内容是个值类型,通过遍历(即list[i]得到的)是该值类型的副本。两层意思:1值类型,定义后就不能修改。2副本,修改对原值不起作用。

当往list中添加的是引用类型时,装在list中的本质上也是个值类型,通过ist[i]只是这个软指针值的副本,即通过这个软指针可以找到在堆中分配的对象(我们能访问软指针所指向的对象的方法,从而修改对象的属性)。但我们不能通过任何方法修改这个软指针副本自身的属性。
当往List中添加的是值类型,是可以通过迭代(list[i]的方式)访问的,但值类型是没有修改方法的(含string类型),当然可以在list[i]的位置上重填入新的值。
结构体,比较特殊,作为值类型,他具有类的很多特性,可以通过“.”,来调用其中的方法。如果允许“list[i].”这样的访问方式来修改值类型,那就违背了集合中副本的限定。本人做了如下尝试:
定义一个结构体:
public struct item
{
public int Id;
public string Nm;

public item(int i,string m)
{
Id = i;
Nm = m;
}
public void Add()
{
Id += 1;
}
public int Get()
{
return this.Id;
}
}

将该结构体item装入list<item> iList后,iList[1].Add()是正确执行的,并且单步跟踪时,“Id+=1”后Id的值较初始化时增加了1,但随后再次调用iList[1].Get(),得到的值依然是初始化时的值。这充分说明了,list[i]得到的只是个副本,对其中的值进行了修改,并不影响集合中的原值。
同时作为值类型,如果允许"iList[1].Id=100"这样的形式存在的话,那么就违背了前面关于副本的限定,所以可以把楼主的问题解释为,编译器强制否定了这种方式。
wolf_xjf 2010-06-18
  • 打赏
  • 举报
回复
综合大家的观点,我结合jbo126的观点,谈点个人理解,list中真正装载的内容是个值类型,通过遍历(即list[i]得到的)是该值类型的副本。两层意思:1值类型,定义后就不能修改。2副本,修改对原值不起作用。

当往list中添加的是引用类型时,装在list中的本质上也是个值类型,通过ist[i]只是这个软指针值的副本,即通过这个软指针可以找到在堆中分配的对象(我们能访问软指针所指向的对象的方法,从而修改对象的属性)。但我们不能通过任何方法修改这个软指针副本自身的属性。
当往List中添加的是值类型,是可以通过迭代(list[i]的方式)访问的,但值类型是没有修改方法的(含string类型),当然可以在list[i]的位置上重填入新的值。
结构体,比较特殊,作为值类型,他具有类的很多特性,可以通过“.”,来调用其中的方法。如果允许“list[i].”这样的访问方式来修改值类型,那就违背了集合中副本的限定。本人做了如下尝试:
定义一个结构体:
public struct item
{
public int Id;
public string Nm;

public item(int i,string m)
{
Id = i;
Nm = m;
}
public void Add()
{
Id += 1;
}
public int Get()
{
return this.Id;
}
}

将该结构体item装入list<item> iList后,iList[1].Add()是正确执行的,并且单步跟踪时,“Id+=1”后Id的值较初始化时增加了1,但随后再次调用iList[1].Get(),得到的值依然是初始化时的值。这充分说明了,list[i]得到的只是个副本,对其中的值进行了修改,并不影响集合中的原值。
同时作为值类型,如果允许"iList[1].Id=100"这样的形式存在的话,那么就违背了前面关于副本的限定,所以可以把楼主的问题解释为,编译器强制否定了这种方式。
小D 2010-06-18
  • 打赏
  • 举报
回复
这种问题还是不看的好,,,,错误的理解,造成错误的思维,,,最后就会误入歧途,走火入魔
idot 2010-06-18
  • 打赏
  • 举报
回复
[Quote=引用 16 楼 vrc289 的回复:]

引用 11 楼 idot 的回复:
引用 9 楼 vrc289 的回复:

引用 7 楼 idot 的回复:
按以下方法可以解决:

1.先定义一个接口

C# code

public interface IListItem {
int ID {get;set;}
string DisplayText {get;set;}
}



2. 然后让Item继承于……
[/Quote]
在《Inseide C# (second edition)》(中译名《C#技术揭秘》,马朝晖译)第17章末尾里有例子和原话,因为绝版了,你是看不到了。
jbo126 2010-06-18
  • 打赏
  • 举报
回复
实在是。。。!唉!
healer_kx 2010-06-18
  • 打赏
  • 举报
回复
[Quote=引用 31 楼 wuyazhe 的回复:]

有倒分贴嫌疑。不过无所谓了。
[/Quote]

是啊,记住这个人,以后不回答他的问题就OK了。
兔子-顾问 2010-06-18
  • 打赏
  • 举报
回复
有倒分贴嫌疑。不过无所谓了。
idot 2010-06-18
  • 打赏
  • 举报
回复
[Quote=引用 29 楼 wolf_xjf 的回复:]

综合大家的观点,我结合jbo126的观点,谈点个人理解,list中真正装载的内容是个值类型,通过遍历(即list[i]得到的)是该值类型的副本。两层意思:1值类型,定义后就不能修改。2副本,修改对原值不起作用。

当往list中添加的是引用类型时,装在list中的本质上也是个值类型,通过ist[i]只是这个软指针值的副本,即通过这个软指针可以找到在堆中分配的对象(我们能访问软指针所指向的对象……
[/Quote]
纯粹是你的一厢情愿,你还是去看看MSIL的代码是怎么写的吧。
  • 打赏
  • 举报
回复
换吧...
ygwilson 2010-06-17
  • 打赏
  • 举报
回复
支持idot 的观点。
healer_kx 2010-06-17
  • 打赏
  • 举报
回复
大致看了一下,我是来支持wuyazhe的!
VRC289 2010-06-17
  • 打赏
  • 举报
回复
[Quote=引用 17 楼 wuyazhe 的回复:]
引用 16 楼 vrc289 的回复:
请问一下上面这句话在MSDN上有相关的解释吗 我好像之前没有听说过这种解释,能给个权威的链接吗?

http://topic.csdn.net/u/20100617/15/2ee2ee3f-4821-41bd-ba8d-876278ac36c2.html

21楼有msdn的连接。
[/Quote]

这个连接只是说明值类型和引用类型作为参数的不同,不能说明“你不能直接访问List中存储的值类型
这句话
jbo126 2010-06-17
  • 打赏
  • 举报
回复
我只是想知道如下:
public struct Item
{
public int Id;
public string DisplayText;
}
Item item1 ;
item1.Id = 0; // 这个地方可以通过. 成员 来赋值
item1.DisplayText = "水星";

那为什么items[0] 是一个结构体,items[0].DisplayText 是给这个结构体赋值 为什么这个地方不行了()
显然,你如果看一下泛型List<>的定义的话就应该明白,item[0]这个用法是对应于属性this[int index]这个定义的,注意它是一个属性,即它是通过get“方法”取得的中间值,上面的item1是Item结构的公共字段,它在整个实例内部是有确切的存储空间的,由于结构值传递的特性,对属性(中间值)进行赋值是没有任何意义的,所以C#编译器干粹进行了强制干预!不允许这样!前段时间的学习过程中经常遇到的情况,应该不难理解吧!就比如你对控件或窗体的Location属性,如果你XXX.Location=new Point(...);那没问题,但你如果是XXX.Location.X=##;那就不行了!



操你妈的CSDN,自己写的贴子确不能编译!!!!
jbo126 2010-06-17
  • 打赏
  • 举报
回复
我只是想知道如下:
public struct Item
{
public int Id;
public string DisplayText;
}
Item item1 ;
item1.Id = 0; // 这个地方可以通过. 成员 来赋值
item1.DisplayText = "水星";

[color=#FF0000]那为什么items[0] 是一个结构体,items[0].DisplayText 是给这个结构体赋值 为什么这个地方不行了()
color]

显然,你如果看一下泛型List<>的定义的话就应该明白,item[0]这个用法是对应于属性this[int index]这个定义的,注意它是一个属性,即它是通过get“方法”取得的中间值,上面的item1是Item结构的公共字段,它在整个实例内部是有确切的存储空间的,由于结构值传递的特性,对属性(中间值)进行赋值是没有任何意义的,所以C#编译器干粹进行了强制干预!不允许这样!前段时间的学习过程中经常遇到的情况,应该不难理解吧!就比如你对控件或窗体的Location属性,如果你XXX.Location=new Point(...);那没问题,但你如果是XXX.Location.X=##;那就不行了!
兔子-顾问 2010-06-17
  • 打赏
  • 举报
回复
[Quote=引用 16 楼 vrc289 的回复:]
请问一下上面这句话在MSDN上有相关的解释吗 我好像之前没有听说过这种解释,能给个权威的链接吗?
[/Quote]
http://topic.csdn.net/u/20100617/15/2ee2ee3f-4821-41bd-ba8d-876278ac36c2.html

21楼有msdn的连接。
VRC289 2010-06-17
  • 打赏
  • 举报
回复
[Quote=引用 11 楼 idot 的回复:]
引用 9 楼 vrc289 的回复:

引用 7 楼 idot 的回复:
按以下方法可以解决:

1.先定义一个接口

C# code

public interface IListItem {
int ID {get;set;}
string DisplayText {get;set;}
}



2. 然后让Item继承于这个接口


C# code

……
[/Quote]

在调用语句 items[0].DisplayText = "水星"时,执行却是先将 items[0] 拆箱到一个临时变量 temp 里 ,因为 temp 不能被访问,所以这里的一个关键点是
你不能直接访问List中存储的值类型

请问一下上面这句话在MSDN上有相关的解释吗 我好像之前没有听说过这种解释,能给个权威的链接吗?

Red_angelX 2010-06-17
  • 打赏
  • 举报
回复
List [] index const.
wuyq11 2010-06-17
  • 打赏
  • 举报
回复
放入List <T>,相当于添加到一个数组
对Class以及对象所建立编程概念,不适应Structure
加载更多回复(13)

110,499

社区成员

发帖
与我相关
我的任务
社区描述
.NET技术 C#
社区管理员
  • C#
  • Web++
  • by_封爱
加入社区
  • 近7日
  • 近30日
  • 至今
社区公告

让您成为最强悍的C#开发者

试试用AI创作助手写篇文章吧