【问题标题】:Replace a object in a list of objects替换对象列表中的对象
【发布时间】:2015-01-01 09:24:04
【问题描述】:

在 C# 中,如果我有一个 List<T>,并且我有一个 T 类型的对象,如何将 List<T> 中的特定项目替换为 T 类型的对象?

这是我尝试过的:

List<CustomListItem> customListItems = new List<CustomListItem>();
CustomListItem customListItem1 = new CustomListItem() { name = "Item 1", date = DateTime.MinValue};
CustomListItem customListItem2 = new CustomListItem() { name = "Item 2", date = DateTime.MinValue };
CustomListItem customListItem3 = new CustomListItem() { name = "Item 3", date = DateTime.MinValue };

customListItems.Add(customListItem1);
customListItems.Add(customListItem2);
customListItems.Add(customListItem3);

CustomListItem newCustomListItem = new CustomListItem() { name = "Item 4", date = DateTime.Now };

customListItem2 = customListItems.Where(i=> i.name == "Item 2").First();
customListItem2 = newCustomListItem;

在上面的代码中,我想用newCustomListItem替换customListItem2

我是否必须删除列表中的项目,然后插入新项目?我可以不做customListItem2 = newCustomListItem的简单分配吗?

用另一个项目替换列表中的项目最有效的方法是什么?

提前致谢

【问题讨论】:

  • 除了已经发布的解决方案之外,您还应该为您的自定义类型实现 Equals/GetHashCode,否则您的 Where/Index 操作可能无法找到正确的元素。

标签: c# .net list variable-assignment generic-list


【解决方案1】:

您必须替换项目,而不是 customListItem2 的值。只需替换以下内容:

customListItem2 = customListItems.Where(i=> i.name == "Item 2").First();
customListItem2 = newCustomListItem;

有了这个:

customListItem2 = customListItems.Where(i=> i.name == "Item 2").First();
var index = customListItems.IndexOf(customListItem2);

if(index != -1)
    customListItems[index] = newCustomListItem;

编辑:

正如Roman R. 在评论中所说,您可以将.Where(predicate).First() 替换为简单的First(predicate)

customListItem2 = customListItems.First(i=> i.name == "Item 2");

【讨论】:

  • 您可以首先将 .Where 替换为 .First 并从末尾删除。这将是相同的结果,但代码更简洁、更快。像下面这样:customListItem2 = customListItems.First(i=> i.name == "Item 2");
  • 使用此代码,索引是否可能为-1?毕竟,如果它不在列表中,First() 会先抛出错误:)
  • 然后使用FirstOrDefault(),如果找不到则返回null,然后为IndexOf(null)返回-1
  • @RomanR。 - 它只会让它更干净;已经证明First(expresion)Where(expresion).First() 慢。有用链接:stackoverflow.com/questions/8663897/…
  • @HellBaby 有趣的观察 - Visual Studio 会尝试强调它并建议您使用 .First 而不是 .Where().First 尽管速度很慢
【解决方案2】:
var customListItems = new List<CustomListItem>();
var customListItem1 = new CustomListItem() { name = "Item 1", date = DateTime.MinValue };
var customListItem2 = new CustomListItem() { name = "Item 2", date = DateTime.MinValue };
var customListItem3 = new CustomListItem() { name = "Item 3", date = DateTime.MinValue };

customListItems.Add(customListItem1);
customListItems.Add(customListItem2);
customListItems.Add(customListItem3);

var newCustomListItem = new CustomListItem() { name = "Item 4", date = DateTime.Now };

customListItems[customListItems.FindIndex(x => x.name == "Item 2")] = newCustomListItem;

public static class ListExtensions
{
    public static void Replace<T>(this List<T> list, Predicate<T> oldItemSelector , T newItem)
    {
        //check for different situations here and throw exception
        //if list contains multiple items that match the predicate
        //or check for nullability of list and etc ...
        var oldItemIndex = list.FindIndex(oldItemSelector);
        list[oldItemIndex] = newItem;
    }
}

然后

customListItems.Replace(x => x.name == "Item 2", newCustomListItem);

【讨论】:

  • 扩展方法是否使用反射?这段代码肯定更方便,我走这条路。只是想知道任何性能损失。
  • 不,它不使用反射。 @dunwan
  • 使用此解决方案,列表仅循环一次。在接受的答案中出现两次。但通常或通常应该处理未找到的情况。如果未找到该项目 FindIndex 返回 -1。
【解决方案3】:

如果列表的顺序对你来说不重要,那么 你可以试试这个

CustomListItem newCustomListItem = new CustomListItem() { name = "Item 4", date = DateTime.Now };

customListItem2 = customListItems.Where(i=> i.name == "Item 2").First();  

customListItems.Remove(customListItem2);
customListItems.Add(newCustomListItem );

【讨论】:

    【解决方案4】:

    您可以使用 select 来构建一个新的 iEnumerable 并替换项:

    customListItems.Select(x => x.name == "Item 2" ? newItem : x).ToList();
    

    我没有测试性能,但我发现这个解决方案非常简洁明了。

    【讨论】:

      【解决方案5】:

      另一种方法是使用DynamicData 包,该包在列表中有很多其他设施:

      快速解答

      确保安装包:

      Install-Package DynamicData -Version 7.4.3
      

      然后

      using DynamicData;
      
      DynamicData.ListEx.Replace<CustomListItem>(customListItems, customListItem2, newCustomListItem);
      

      以下是完整的工作解决方案。

      工作示例

      我的自定义物品类

      public class CustomListItem
      {
          public string name; 
          public DateTime date;
      
          public override string ToString()
          {
              return name + ", " + date.ToString();
          }
      }
      

      在main中试试这个,确保在替换之前注释掉赋值。

      static void Main(string[] args)
      {
          List<CustomListItem> customListItems = new List<CustomListItem>();
          CustomListItem customListItem1 = new CustomListItem() { name = "Item 1", date = DateTime.MinValue };
          CustomListItem customListItem2 = new CustomListItem() { name = "Item 2", date = DateTime.MinValue };
          CustomListItem customListItem3 = new CustomListItem() { name = "Item 3", date = DateTime.MinValue };
      
          customListItems.Add(customListItem1);
          customListItems.Add(customListItem2);
          customListItems.Add(customListItem3);
      
          CustomListItem newCustomListItem = new CustomListItem() { name = "Item 4", date = DateTime.Now };
      
          customListItem2 = customListItems.Where(i => i.name == "Item 2").First();
          //customListItem2 = newCustomListItem;
      
      
          DynamicData.ListEx.Replace<CustomListItem>(customListItems, customListItem2, newCustomListItem);
      
          foreach(var customListItem in customListItems)
          {
              Debug.Print("\n" + customListItem.ToString());
          }
      }
      
      references: https://github.com/reactivemarbles/DynamicData.
      https://dynamic-data.org
      

      【讨论】:

        猜你喜欢
        • 1970-01-01
        • 1970-01-01
        • 1970-01-01
        • 2012-11-23
        • 2014-02-12
        • 2018-06-17
        • 2016-10-20
        • 1970-01-01
        • 1970-01-01
        相关资源
        最近更新 更多