【问题标题】:C# List<T>.Add not adding properlyC# List<T>.Add 未正确添加
【发布时间】:2015-04-30 06:47:35
【问题描述】:

使用.Add 将类的实例添加到泛型列表不起作用。

为了说明问题,这里有两个简单的示例类:

public class WorkOrder
{
    private List<Note> _Notes;
    public List<Note> Notes
    {
        get
        {
            return _Notes ?? new List<Note>();
        }
        set
        {
            _Notes = value;
        }
    }
}

public class Note
{
    public string NoteText { get; set; }
    public System.DateTime Time { get; set; }
    public string User { get; set; }
}

您可能会注意到WorkOrder.Notes 属性上get 中的编码。我把它放进去,这样属性就不会被初始化为空值(参考我在 SO here 上发布的另一个问题的答案)。

要使用这些类:

public void Test()
{
    WorkOrder tempWorkOrder = new WorkOrder();
    Note tempNote = new Note()
    {
        User = "Aaron",
        Time = DateTime.Now,
        NoteText = "Work Order pulled from CSV Excel report."
    };
    tempWorkOrder.Notes.Add(tempNote);
}

我希望Test() 中的最后一行将tempNote 添加到tempWorkOrder 中的Note 列表中。但是,tempWorkOrder.Notes 在此行完成后为空。不会抛出任何错误或异常。

我正在使用 VS2013 Express。

我做错了什么?

【问题讨论】:

  • 我不会懒惰地创建列表的实例 - 你可能会导致一些意想不到的行为。
  • 返回_Notes ??新列表();如果 _Notes 不为 null,则执行以下操作,如果为 NULL,则创建并返回一个新列表,但从未分配给 _Notes。

标签: c#


【解决方案1】:

如果您使用 C# 8,应该可以使用 null-coalescing assignment,如下所示:

get =&gt; _Notes ??= new List&lt;Note&gt;();

带括号:

get { return _Notes ??= new List<Note>(); }

【讨论】:

    【解决方案2】:

    晚会,你可以创建一个小扩展方法,可以防止 null 或空列表:

    public static bool NotNullAndEmpty<T>(this IEnumerable<T> source)
    {
      if (source != null && source.Any())
        return true;
      else
        return false;
    }
    

    另外,如果您使用的是数据库,那么建议使用IEnumerable 并使用IEnumerable 进行所有修改。完成后,调用.ToList(),这将导致对数据库的一次调用。

    【讨论】:

      【解决方案3】:
      private List<Note> _Notes;
      public List<Note> Notes
      {
          get
          {
              return _Notes ?? new List<Note>();
          }
          set
          {
              _Notes = value;
          }
      }
      

      get 是错误的。应该是:

          get
          {
              if (_Notes == null) {
                  _Notes = new List<Note>();
              }
              return _Notes;
          }
      

      因为否则您不会保存您创建的new List&lt;Note&gt;(),并且每次使用get 时都会重新创建它(get 返回一个new List&lt;Note&gt;() 但不会修改_Notes,所以每个@ 987654330@ 检查_Notes,看到它是null 并返回new List&lt;Note&gt;())

      请注意,如果您讨厌这个世界(以及您的程序员同行),您可以将 get 压缩为:

      return _Notes ?? (_Notes = new List<Note>());
      

      (见Ternary/null coalescing operator and assignment expression on the right-hand side?)我对这个世界(和我的程序员同事)的仇恨还不够:-)

      【讨论】:

      • 就是这样。看起来到目前为止所有的答案都是正确的,但这个答案最完整。谢谢大家!
      • 我没有得到对return _Notes ?? (_Notes = new List&lt;Note&gt;()); 的“仇恨”。我认为它非常清楚,事实上我更喜欢它而不是单行if。链接的线程显示了一些滥用的可能性,但您几乎可以滥用所有内容。
      • @Corak 单行如果是因为我直接在不自动缩进的 SO 编辑器中编写 :-) 我总是使用 if {}= 在另一个表达式中的“讨厌”可能与“一行,一种效果”的代码编写方式和/或由 C 中的 = 赋值与 == 相等运算符引起的大量错误有关/C++
      • @xanatos - 谢谢,我明白了。好吧,有了“一行,一个效果”,Linq 方法链接不会走得太远。 ^_^;
      • @Corak 不,因为 Linq 方法没有副作用(链的每个成员都会产生新的东西),所以它们没问题。
      【解决方案4】:
      public class WorkOrder
      {
          public List<Note> Notes {get;set;}
      
          public WorkOrder()
          {
              Notes = new List<Note>();
          }
      }
      

      但在 C# 6.0 中,您应该能够执行以下操作:

      public class WorkOrder
      {
          public List<Note> Notes {get;set;} = new List<Note>();                
      }
      

      【讨论】:

        【解决方案5】:

        您没有初始化_Notes

        因此,当_Notes 为空时,当您返回List&lt;Note&gt; 时,它并没有将对象分配给_Notes。每次访问公共属性时,它都会返回一个不同的 List&lt;Note&gt;,这就是 Add() 调用似乎不起作用的原因。

        你应该使用:

        get 
        { 
          if (_Notes == null) 
             _Notes = new List<Note>(); 
          return _Notes; 
        }
        

        【讨论】:

          【解决方案6】:

          问题出在你的 get 方法上:

              get
              {
                  return _Notes ?? new List<Note>();
              }
          

          由于您没有将要创建的对象的引用分配给_Notes,因此它一直为空,并且您分配给了一个在其他任何地方都没有引用的列表。

          你可以这样做:

              get
              {
                  if (_Notes == null)
                      _Notes = new List<Note>();
                  return _Notes;
              }
          

          【讨论】:

            【解决方案7】:

            Notes 的getter 中,您没有做任何事情来保存对新创建列表的引用。因此,每次访问该 getter 时,都会得到一个新的空列表。所以这个:

            tempWorkOrder.Notes.Add(tempNote);
            

            ...正在将tempNote 添加到立即丢弃的List&lt;Note&gt;

            【讨论】:

              【解决方案8】:

              你永远不会分配_Notes

              改为这样做

                  private List<Note> _Notes;
                  public List<Note> Notes
                  {
                      get
                      {
                          if(_Notes == null)
                               _Notes = new List<Note>();
                          return _Notes;
                      }
                      set
                      {
                          _Notes = value;
                      }
                  }
              

              【讨论】:

                【解决方案9】:

                您尚未在此处创建列表。您需要将构造函数添加到 WorkOrder,因为您无法添加到不存在的集合中。这样,每当您创建工单时,“_Notes”字段中都会有一个空列表。

                它看起来像这样:

                WorkOrder(){
                    _Notes = new List<Note>();
                }
                

                【讨论】:

                • 在 EF 中(这是 OP 在另一个问题中所做的)这是初始化 ICollections 的正常方式。
                • 只是想指出:这个问题是针对与 EF 无关的类 - 虽然是的,但它可以很容易地应用于 Code First 情况。
                猜你喜欢
                • 1970-01-01
                • 2023-04-05
                • 1970-01-01
                • 1970-01-01
                • 1970-01-01
                • 2021-11-12
                • 2016-05-18
                • 1970-01-01
                • 1970-01-01
                相关资源
                最近更新 更多