【问题标题】:When I try to load the list my values come out all the same当我尝试加载列表时,我的值都一样
【发布时间】:2016-11-28 03:29:37
【问题描述】:

我有一个简单的课程:

private class ColumnAndValue
{
    private string columnName;
    private string columnValue;

    public string ColumnName
    {
        get { return columnName; }
        set { columnName = value.Trim(); }
    }
    public string ColumnValue
    {
        get { return columnValue; }
        set { columnValue = value.Trim(); }
    }
}

以及该类的列表:

private List<ColumnAndValue> cvList = new List<ColumnAndValue>();

这就是我的问题所在,当我尝试加载列表时,我的值都一样。这是我加载它的方式:

public void TestList()
{
    ColumnAndValue cv = new ColumnAndValue();
    cv.ColumnName = "Column 1";
    cv.ColumnValue = "Value 1";
    cvList.Add(cv);

    cv.ColumnName = "Column 2";
    cv.ColumnValue = "Value 2";
    cvList.Add(cv);

    cv.ColumnName = "Column 3";
    cv.ColumnValue = "Value 3";
    cvList.Add(cv);

    cv.ColumnName = "Column 4";
    cv.ColumnValue = "Value 4";
    cvList.Add(cv);

    List<string> c1 = new List<string>();

    c1 = cvList.Select(c => c.ColumnName).ToList();

    foreach (object obj in c1)
    {
        Console.WriteLine(obj.ToString());
    }

}

当我运行这段代码时,我得到了四次迭代:

Column 4
Column 4
Column 4
Column 4

当我在调试时进入每个 cvList.Add 时,第一次迭代显示 cvList 具有正确的信息,第二个两个值都有第 2 列......所以,我这样尝试:

ColumnAndValue cv1 = new ColumnAndValue();
ColumnAndValue cv2 = new ColumnAndValue();
ColumnAndValue cv3 = new ColumnAndValue();
ColumnAndValue cv4 = new ColumnAndValue();

cv1.ColumnName = "Column 1";
cv1.ColumnValue = "Value 1";
cvList.Add(cv1);

cv2.ColumnName = "Column 2";
cv2.ColumnValue = "Value 2";
cvList.Add(cv2);

cv3.ColumnName = "Column 3";
cv3.ColumnValue = "Value 3";
cvList.Add(cv3);

cv4.ColumnName = "Column 4";
cv4.ColumnValue = "Value 4";
cvList.Add(cv4);

一切都按预期进行:

Column 1
Column 2
Column 3
Column 4

我的问题是,如何在仅使用一个 cv 实例的情况下填写列表?

【问题讨论】:

  • List&lt;T&gt;.Add(T item) 不会将item副本 添加到列表中,而是添加一个引用。 4 个列表项都指向同一个原始对象(您同时更新了)
  • 你需要重新实例化第一组代码中的对象,否则你只是重新添加相同的项目4次。

标签: c# list


【解决方案1】:

您应该使用新实例。如果您将一个项目添加到列表中,然后更改项目属性,那么您只是在编辑同一个项目。再次将其添加到列表中只会添加对该项目的另一个引用。

这是一个简单的方法:

private class ColumnAndValue
{
    private string columnName;
    private string columnValue;

    public string ColumnName
    {
        get { return columnName; }
        set { columnName = value.Trim(); }
    }
    public string ColumnValue
    {
        get { return columnValue; }
        set { columnValue = value.Trim(); }
    }
    public ColumnAndValue(string colName, string colValue)
    {
       columnName = colName;
       columnValue = colValue;
    }
}

然后使用以下命令创建您的列表:

private List<ColumnAndValue> cvList = new List<ColumnAndValue>();
cvList.Add(new ColumnAndValue("Column 1", "Value 1"));
cvList.Add(new ColumnAndValue("Column 2", "Value 2"));
cvList.Add(new ColumnAndValue("Column 3", "Value 3"));
cvList.Add(new ColumnAndValue("Column 4", "Value 4"));

【讨论】:

    【解决方案2】:

    您会看到,ListT.Add 方法不会创建其引用参数的副本。它只是复制并存储对您的类(相同)实例的引用四次。

    这是 ListT 的代码。添加 referencesource.microsoft.com

    你的问题是矛盾的,如果你只想要一个实例,你只会得到一个实例。 ListT.Add 不复制(不创建引用类型的实例)。

    【讨论】:

    • 哇,那是金矿!感谢您的链接!
    • 没问题。 referencesource 是我去的 msdn 之后的第二个地方,当我开始觉得有些不对劲时)
    【解决方案3】:

    改成:

    public void TestList()
    {
        cvList.Add(new ColumnAndValue(){ColumnName = "Column 1", ColumnValue = "Value 1"});
        cvList.Add(new ColumnAndValue(){ColumnName = "Column 2", ColumnValue = "Value 2"});
        cvList.Add(new ColumnAndValue(){ColumnName = "Column 3", ColumnValue = "Value 3"});
        cvList.Add(new ColumnAndValue(){ColumnName = "Column 4", ColumnValue = "Value 4"});
    
        List<string> c1 = cvList.Select(c => c.ColumnName).ToList();
    
        foreach (object obj in c1)
        {
            Console.WriteLine(obj.ToString());
        }
    
    }
    

    因为 List.Add() 添加对您的 ColumnAndValue 实例的引用而不是副本,因此,如果您更改对象的名称,它将在您使用它的所有地方都发生更改。

    您可以在 MSDN 上的 When is a C# value/object copied and when is its reference copied?Value Types and Reference Types 阅读更多信息

    【讨论】:

    • 完美@ThomasAyoub!我刚试了一下,效果很好。问题:我不知道如何搜索。你能告诉我这叫什么吗?
    • 啊!我明白了,最初,我是通过引用传递的,所以最后,引用不仅改变了值,而且我添加了四次。反响很好!再次感谢。
    【解决方案4】:

    你每次都需要更新你的简历

    public void TestList()
        {
            ColumnAndValue cv = new ColumnAndValue();
            cv.ColumnName = "Column 1";
            cv.ColumnValue = "Value 1";
            cvList.Add(cv);
    
    cv = new ColumnAndValue();
            cv.ColumnName = "Column 2";
            cv.ColumnValue = "Value 2";
            cvList.Add(cv);
    
    cv = new ColumnAndValue();
            cv.ColumnName = "Column 3";
            cv.ColumnValue = "Value 3";
            cvList.Add(cv);
    
    cv = new ColumnAndValue();
            cv.ColumnName = "Column 4";
            cv.ColumnValue = "Value 4";
            cvList.Add(cv);
    
            List<string> c1 = new List<string>();
    
            c1 = cvList.Select(c => c.ColumnName).ToList();
    
            foreach (object obj in c1)
            {
                Console.WriteLine(obj.ToString());
            }
    
        }
    

    cv 是对象的句柄。如果您不每次都更新它,那么您只是在更改同一个对象的值。你的 cvlist 只是引用了同一个对象,你每次都更新它

    【讨论】:

      【解决方案5】:

      当您将 cv 对象添加到列表中时,列表会维护对该对象的 引用

      这意味着您一遍又一遍地将同一个对象添加到列表中,并更改同一个对象。

      您要做的是向列表中添加不同的变量:

      public void TestList()
      {
          ColumnAndValue cv = new ColumnAndValue();
          cv.ColumnName = "Column 1";
          cv.ColumnValue = "Value 1";
          cvList.Add(cv);
      
          // Now cv is a reference to a different object
          cv = new ColumnAndValue();
          cv.ColumnName = "Column 2";
          cv.ColumnValue = "Value 2";
          cvList.Add(cv);
      
          // Now cv is a reference to a different object
          cv = new ColumnAndValue();
          cv.ColumnName = "Column 3";
          cv.ColumnValue = "Value 3";
          cvList.Add(cv);
      
          // Now cv is a reference to a different object
          cv = new ColumnAndValue();
          cv.ColumnName = "Column 4";
          cv.ColumnValue = "Value 4";
          cvList.Add(cv);
      
          List<string> c1 = new List<string>();
      
          c1 = cvList.Select(c => c.ColumnName).ToList();
      
          foreach (object obj in c1)
          {
              Console.WriteLine(obj.ToString());
          }
      
      }
      

      【讨论】:

        【解决方案6】:

        发生的事情是您的List 实际上是对您已创建的同一对象的引用 列表。您不断更改同一对象中的数据,但不断在列表中添加对它的引用。

        欲了解更多信息,check the difference between reference types and value types

        编辑:对此效果有一个答案,但由于某种原因被删除。您期望的行为是使用 struct 关键字定义的值类型。值类型是按值复制的。因此,在您的代码中,将 class 更改为 struct 会达到您期望的行为。 请记住它仍然是看起来很奇怪的代码,您应该习惯于用新数据实例化新对象,因为这样您的代码会更具可读性。更改现有对象中的数据的能力,即 Mutability,是一个强大的工具,但正如您刚刚发现的那样,使用它很容易被滥用和出错。

        【讨论】:

          猜你喜欢
          • 2023-01-24
          • 2021-05-12
          • 1970-01-01
          • 2021-11-27
          • 1970-01-01
          • 1970-01-01
          • 1970-01-01
          • 2015-12-11
          • 2018-07-25
          相关资源
          最近更新 更多