【问题标题】:Can't change struct's members value inside generic collections无法在泛型集合中更改结构的成员值
【发布时间】:2013-02-18 20:55:10
【问题描述】:

想象一下struct

        struct Person
        {
             public string FirstName { get; set; }
             public string LastName { get; set; }
        }

以下代码:

        var list = new List<Person>();
        list.Add(new Person { FirstName = "F1", LastName = "L1" });
        list.Add(new Person { FirstName = "F2", LastName = "L2" });
        list.Add(new Person { FirstName = "F3", LastName = "L3" });

        // Can't modify the expression because it's not a variable
        list[1].FirstName = "F22";

当我想更改 Property 的值时,会出现以下错误:

Can't modify the expression because it's not a variable

虽然,当我尝试在 Person[] 等数组中更改它时,它没有任何错误。使用泛型集合时我的代码有什么问题吗?

【问题讨论】:

  • 为什么你的人不上课?
  • 拥有一个可变结构不是一个好主意——你不能使用一个类吗?
  • 试试看 - 这是 MSDN 上的解释:msdn.microsoft.com/query/… - 这是因为值类型是在赋值时复制的......当您从属性或索引器中检索值类型时,您会得到对象的副本,而不是对对象本身的引用。
  • 对于完全相同的问题已经有了很好的答案stackoverflow.com/questions/414981/…
  • @Ginosaji 你怎么知道的,如果General rule of thumb is to never use struct

标签: c# .net struct generic-list generic-collections


【解决方案1】:

当您通过List[] 索引器返回struct 时,它会返回条目的副本。所以如果你在那里分配了FirstName,它就会被扔掉。因此编译器错误。

要么将您的 Person 重写为引用类型 class,要么进行完全重新分配:

Person person = list[1];
person.FirstName = "F22";
list[1] = person;

一般来说,可变结构会带来诸如此类的问题,这些问题可能会在未来引起头痛。除非您有充分的理由使用它们,否则您应该强烈考虑更改您的 Person 类型。

Why are mutable structs “evil”?

【讨论】:

  • +1 您展示的读/修改/写回方法是恕我直言,这是最简洁的编码方式,因为它使结构的哪些部分发生了变化非常明显。像list[1] = new Person("F22", list[1].LastName); 这样的代码远没有那么清晰,而且更有可能是错误的[例如。如果Person 有一个可选的MiddleName 参数,后面的代码会意外清除它--oops]。
【解决方案2】:

显然,部分问题仍未得到解答。 List&lt;Person&gt;Person[] 有什么区别。在按索引获取元素方面,List 调用返回值类型实例副本的索引器(方法),在相反的数组中按索引返回的不是副本,而是指向索引处元素的托管指针(使用特殊的 IL 指令 ldelema )。

当然,如其他答案中所述,可变值类型是邪恶的。看一个简单的例子。

var en = new {Ints = new List<int>{1,2,3}.GetEnumerator()};
while(en.Ints.MoveNext())
{
    Console.WriteLine(x.Ints.Current);
}

惊讶吗?

【讨论】:

    【解决方案3】:

    像这样重做你的struct

        struct Person
        {
             private readonly string firstName;
             private readonly string lastName;
             public Person(string firstName, string lastName)
             {
                 this.firstName = firstName;
                 this.lastName = lastName;
             }
             public string FirstName { get { return this.firstName; } }
             public string LastName { get { return this.lastName; } }
        }
    

    以下代码为:

        var list = new List<Person>();
        list.Add(new Person("F1", "L1"));
        list.Add(new Person("F2", "L2"));
        list.Add(new Person("F3", "L3"));
    
        // Can modify the expression because it's a new instance
        list[1] = new Person("F22", list[1].LastName);
    

    这是由于struct 的复制语义。使其不可变并在这些约束内工作,问题就会消失。

    【讨论】:

    • 我不喜欢“不可变”的方法,因为必须检查结构的代码才能知道list[1] = new Person("F22", list[1].LastName); 的效果是什么。例如,如果Person 类包含一个MiddleName 字段,该字段将由可选的MiddleName 参数初始化,则上述语句将(可能意外地)删除list[1].MiddleName。相比之下,如果 LastName 是一个暴露的 字段,那么知道这足以确定 Chris Sinclair 答案中的代码不会影响其他任何内容。
    猜你喜欢
    • 1970-01-01
    • 1970-01-01
    • 2016-11-05
    • 1970-01-01
    • 1970-01-01
    • 1970-01-01
    • 1970-01-01
    • 1970-01-01
    • 1970-01-01
    相关资源
    最近更新 更多