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#编译器的设计,所以不能像类一样用.号引用成员!” 这是什么意思
例如ClassA a=new ClassA(); ClassA otherA=a;
//这里a和otherA都是引用类型,他们引用的都是新建的ClassA对象的地址。
//本质上说,a和otherA的内存空间中,存放的不是创建的对象的内容,而是指向对象内容存放空间的首地址!而值类型是在stack中分配内存的。C# code //这里使用的是结构,故不能直接用 items[1].DisplayText = "金星";,如果 Item 是类,则可以直接用。 //为什么呢?因为结构是按值传递的。
http://topic.csdn.net/u/20100617/15/2ee2ee3f-4821-41bd-ba8d-876278ac36c2.html
int ID {get;set;}
string DisplayText {get;set;}
}
2. 然后让Item继承于这个接口
public struct Item : IListItem{
////
}这样你的代码就可以运行了。
我就是看了你刚才回复的那个帖子我只是想知道如下:
public struct Item
{
public int Id;
public string DisplayText;
}Item item1 ;
item1.Id = 0; // 这个地方可以通过. 成员 来赋值
item1.DisplayText = "水星";那为什么items[0] 是一个结构体,items[0].DisplayText 是给这个结构体赋值 为什么这个地方不行了()
这个我知道, 我想知道的是为什么不能用 items[0].DisplayText 这种形式赋值
因为结构体是值类型,而ListItem要求的却是引用类型。
调用语句 items.Add(item1) 并不是直接将Item1添加到List中,而却是将item装箱后的结果添加到list中;
在调用语句 items[0].DisplayText = "水星"时,执行却是先将 items[0] 拆箱到一个临时变量 temp 里 ,因为 temp 不能被访问,所以这里的一个关键点是
你不能直接访问List中存储的值类型基于以上原因,编译器就会给出这样的错误:
the left-hand of an assignment must be a variable , property or indexer.
static void Main(string[] args)
{
List<Item> items = new List<Item>(); //List为引用类型,在托管堆中分配内存 //添加
Item item1 = new Item(); //Item为值类型,在线程栈中分配内存,属性"Id" 和 "DisplayText" 均在这里
item1.Id = 0;
item1.DisplayText = "test1";
items.Add(item1); //引发装箱操作,即在托管堆中分配内存,然后把item1复制到该托管内存中,并设置方法指针,同步信息等,使之成为引用类型。至此,托管堆和栈均有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"; // 这个地方会报错误 ---- List不支持这样的访问操作,但是可以通过这样访问item1的方法(如果有的话,而且是执行一次拆箱之后,得到新的栈item1再访问item1的方法)
}
http://topic.csdn.net/u/20100617/15/2ee2ee3f-4821-41bd-ba8d-876278ac36c2.html?seed=1559264053&r=66297615#r_66297615结合msdn,已经给出了答案。自认为是正确的。
这里再强调一下最关键的
1.void Foo(StructValue o)没有人怀疑这里的o是个结构体对象的副本。对吧。这个还有疑问的复习msdn去。2.
StructValue Foo()
{
StructValue f;
return f;//你认为这里返回的是f还是f的副本呢?想明白这个,结合List索引器的get代码,应该明白原因了,不要纠结了,换用类吧。
}这里都明白,返回的f是定义的f的一个副本,没有问题吧。3.看看List<T>的索引器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个例子看懂。这个问题就清晰了。呵呵。
对Class以及对象所建立编程概念,不适应Structure
在调用语句 items[0].DisplayText = "水星"时,执行却是先将 items[0] 拆箱到一个临时变量 temp 里 ,因为 temp 不能被访问,所以这里的一个关键点是
你不能直接访问List中存储的值类型请问一下上面这句话在MSDN上有相关的解释吗 我好像之前没有听说过这种解释,能给个权威的链接吗?
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=##;那就不行了!
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,自己写的贴子确不能编译!!!!
这个连接只是说明值类型和引用类型作为参数的不同,不能说明“你不能直接访问List中存储的值类型
”这句话
{
//定义一个泛型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;
这样不可以的。希望这样说,各位不明白的能明白。明白的更明白。
{
public int value;
public AA(int v)
{
value = v;
}
}
当往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"这样的形式存在的话,那么就违背了前面关于副本的限定,所以可以把楼主的问题解释为,编译器强制否定了这种方式。
当往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"这样的形式存在的话,那么就违背了前面关于副本的限定,所以可以把楼主的问题解释为,编译器强制否定了这种方式。