关于c#:通过id从通用列表中删除对象

Remove object from generic list by id

我有一个这样的域类:

1
2
3
4
5
public class DomainClass
{
  public virtual string name{get;set;}
  public virtual IList<Note> Notes{get;set;}
}

我该如何从IList中删除项目?如果它是一个列表,我可以这样做,但它必须是一个IList,因为我使用NHibernate作为我的坚持层。

理想情况下,我希望在我的域类中有这样的方法:

1
2
3
4
5
6
7
8
9
10
11
public virtual void RemoveNote(int id)
{
   //remove the note from the list here

   List<Note> notes = (List<Note>)Notes

   notes.RemoveAll(delegate (Note note)
   {
       return (note.Id = id)
   });
}

但我不能把IList铸造成List。有没有比这更优雅的方法?


您可以筛选出不需要的项目,然后只使用所需的项目创建一个新列表:

1
2
3
4
5
6
public virtual void RemoveNote(int id)
{
   //remove the note from the list here

   Notes = Notes.Where(note => note.Id != id).ToList();
}


edit2:此方法不需要强制转换为List

1
foreach (var n in Notes.Where(note => note.Id == id).ToArray()) Notes.Remove(n);

或者…

1
Notes.Remove(Notes.Where(note => note.Id == id).First());

第一个是最好的。第二种情况下,如果没有任何票据有id,则会抛出异常。

编辑:感谢马格纳斯和斯巴罗显示了我的错误。


如果您可以更改数据结构,我建议使用Dictionary。比你能做到的还要多:

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
public class DomainClass
{
  public virtual string name{get;set;}
  public virtual IDictionary<int, Note> Notes {get; set;}

  //Helper property to get the notes in the dictionary
  public IEnumerable<Note> AllNotes
  {
    get
    {
      return notes.Select (n => n.Value);
    }
  }

  public virtual void RemoveNote(int id)
  {
     Notes.Remove(id);
  }

}

如果id不是唯一的,则使用IDictionary>


您可以手动对其进行编码。原始实现是o(n*k),其中n是列表中的项数,k是要删除的项数。如果您只想删除一个项目,它会很快。

但是如果您想删除许多项,那么对于许多IList实现(包括List,不知道nhibernate列表的行为如何),本机实现就变成了O(n^2),您需要编写更多的代码来获得O(n)RemoveAll实现。

旧答案中的一个可能实现:列出,不要丢失引用

这个实现的诀窍是,in将保留的项移动到o(n)中列表的开头。然后它会不断删除列表的最后一项(通常是O(1),因为不需要移动任何元素),所以截断会变成O(n)total。这意味着整个算法是O(n)。


请考虑,在某些情况下,为了更好地避免公共虚拟,请使用模板方法模式:

1
2
3
4
5
6
7
8
9
10
 public void Load(IExecutionContext context)
 {
      // Can safely set properties, call methods, add events, etc...
      this.Load(context);            
      // Can safely set properties, call methods, add events, etc.
 }

 protected virtual void Load(IExecutionContext context)
 {
 }


可以接收要删除的项数组。然后从循环中的列表中删除它们。看看这个例子:

1
2
3
4
5
6
7
8
IList<int> list = new List<int> { 1, 2, 3, 4, 5, 1, 3, 5 };

var valuesToRemove = list.Where(i => i == 1).ToArray();

foreach (var item in valuesToRemove)
{
    list.Remove(item);
}