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#编译器的设计,所以不能像类一样用.号引用成员!”   这是什么意思

解决方案 »

  1.   

    没办法,把结构体修改为class吧。用法上一样,占用内存上也一样。
      

  2.   

    所有的引用类型,都是在heap中分配内存的,对引用类型的操作,例如赋值等,都是对引用类型的“指针”进行操作的!
    例如ClassA a=new ClassA(); ClassA otherA=a; 
    //这里a和otherA都是引用类型,他们引用的都是新建的ClassA对象的地址。 
    //本质上说,a和otherA的内存空间中,存放的不是创建的对象的内容,而是指向对象内容存放空间的首地址!而值类型是在stack中分配内存的。C# code    //这里使用的是结构,故不能直接用 items[1].DisplayText = "金星";,如果 Item 是类,则可以直接用。 //为什么呢?因为结构是按值传递的。
      

  3.   

    看看这个帖子,我刚答复的,同样的问题!
    http://topic.csdn.net/u/20100617/15/2ee2ee3f-4821-41bd-ba8d-876278ac36c2.html
      

  4.   

    这个怎么说呢,其实这个不仅仅是Value,还是Ref的问题,还有RValue,LValue的问题
      

  5.   

    其实我觉得你的解释computerfox真的没有切中要害。
      

  6.   

    按以下方法可以解决:1.先定义一个接口public interface IListItem {
        int ID {get;set;}
        string DisplayText {get;set;}
    }
    2. 然后让Item继承于这个接口
    public struct Item : IListItem{
        ////
    }这样你的代码就可以运行了。
      

  7.   


    我就是看了你刚才回复的那个帖子我只是想知道如下:
    public struct Item
    {
      public int Id;
      public string DisplayText;
    }Item item1 ;
    item1.Id = 0;   // 这个地方可以通过. 成员  来赋值
    item1.DisplayText = "水星";那为什么items[0] 是一个结构体,items[0].DisplayText 是给这个结构体赋值 为什么这个地方不行了()
      

  8.   


    这个我知道, 我想知道的是为什么不能用 items[0].DisplayText  这种形式赋值
      

  9.   


    因为结构体是值类型,而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.
      

  10.   


    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的方法)
            }
      

  11.   

    在你的另一个帖子
    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个例子看懂。这个问题就清晰了。呵呵。
      

  12.   

    放入List <T>,相当于添加到一个数组
    对Class以及对象所建立编程概念,不适应Structure
      

  13.   


    在调用语句 items[0].DisplayText = "水星"时,执行却是先将 items[0] 拆箱到一个临时变量 temp 里 ,因为 temp 不能被访问,所以这里的一个关键点是
    你不能直接访问List中存储的值类型请问一下上面这句话在MSDN上有相关的解释吗 我好像之前没有听说过这种解释,能给个权威的链接吗?
      

  14.   

    http://topic.csdn.net/u/20100617/15/2ee2ee3f-4821-41bd-ba8d-876278ac36c2.html21楼有msdn的连接。
      

  15.   

    我只是想知道如下:
    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=##;那就不行了!
      

  16.   

    我只是想知道如下:
    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,自己写的贴子确不能编译!!!!
      

  17.   


    这个连接只是说明值类型和引用类型作为参数的不同,不能说明“你不能直接访问List中存储的值类型
    这句话
      

  18.   

    大致看了一下,我是来支持wuyazhe的!
      

  19.   

    处于好奇好玩,写个例子给大家看看。以证明我上面的分析。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;
    这样不可以的。希望这样说,各位不明白的能明白。明白的更明白。
      

  20.   

    忘记贴了,AA定义public struct AA
    {
        public int value;
        public AA(int v)
        {
            value = v;
        }
    }
      

  21.   

    在《Inseide C# (second edition)》(中译名《C#技术揭秘》,马朝晖译)第17章末尾里有例子和原话,因为绝版了,你是看不到了。
      

  22.   

    综合大家的观点,我结合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"这样的形式存在的话,那么就违背了前面关于副本的限定,所以可以把楼主的问题解释为,编译器强制否定了这种方式。
      

  23.   

    综合大家的观点,我结合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"这样的形式存在的话,那么就违背了前面关于副本的限定,所以可以把楼主的问题解释为,编译器强制否定了这种方式。
      

  24.   

    纯粹是你的一厢情愿,你还是去看看MSIL的代码是怎么写的吧。