为什么使用ICollection而不是IEnumerable或List<T>多多/一对多的关系?

我在教程中经常看到这种情况,导航属性为ICollection<T>

这是实体框架的强制性要求吗?我可以使用IEnumerable吗?

使用ICollection而不是IEnumerable甚至List<T>的主要目的是什么?

212009 次浏览

使用ICollection<T>是因为IEnumerable<T>接口没有提供添加项、删除项或以其他方式修改集合的方法。

使用ICollection的基本思想是提供一个接口来快速访问有限数量的数据。事实上,你有一个ICollection。数属性。IEnumerable更适合于某些数据链,在那里你读到某个逻辑点,某个消费者特别指定的条件或直到枚举的结束。

通常,您的选择取决于您需要访问哪些方法。一般来说——IEnumerable<> (MSDN: http://msdn.microsoft.com/en-us/library/system.collections.ienumerable.aspx)表示只需要遍历的对象列表,ICollection<> (MSDN: http://msdn.microsoft.com/en-us/library/92t2ye13.aspx)表示需要遍历和修改的对象列表,List<>表示需要遍历、修改和排序的对象列表等(完整列表:http://msdn.microsoft.com/en-us/library/6sh2ey19.aspx请参见这里)。

从更具体的角度来看,延迟加载用于选择类型。默认情况下,实体框架中的导航属性带有更改跟踪,并且是代理。为了将动态代理创建为导航属性,虚拟类型必须实现了ICollection

表示关系的“多”端的导航属性必须返回实现ICollection的类型,其中T是关系另一端对象的类型。-创建POCO代理要求MSDN . sh

定义和管理关系的更多信息MSDN

回答你关于List<T>的问题:

List<T>是一个类;指定接口可以使实现更加灵活。一个更好的问题是“为什么不IList<T>?”

要回答这个问题,请考虑IList<T>ICollection<T>: integer索引添加了什么,这意味着条目具有某种任意顺序,并且可以通过引用该顺序来检索。这在大多数情况下可能没有意义,因为项目可能需要在不同的上下文中以不同的顺序排列。

我过去所做的是使用IList<Class>、__abc1或IEnumerable<Class> (if静态列表)声明我的内部类集合,这取决于我是否必须在存储库中的一个方法中执行以下任意数量的操作:枚举,排序/排序或修改。当我只需要对对象进行枚举(或者排序)时,我会创建一个临时__abc3来处理IEnumerable方法中的集合。我认为这种做法只有在收集相对较小的情况下才会有效,但总的来说,这可能是一种很好的做法,idk。如果有证据表明这不是好的做法,请纠正我。

我是这样记得的:

  1. IEnumerable有一个方法GetEnumerator(),它允许读取集合中的值,但不写入集合。使用枚举器的大部分复杂性都由c#中的for each语句解决了。IEnumerable有一个属性:Current,它返回当前元素。

  2. ICollection实现了IEnumerable,并添加了一些额外的属性,其中最常用的是Count。ICollection的泛型版本实现了Add()和Remove()方法。

  3. IList实现了IEnumerable和ICollection,并添加了对项目的整数索引访问(这通常不是必需的,因为在数据库中进行排序)。

ICollection和IEnumerable之间有一些基本的区别

  • < em > IEnumerable < / em > -只包含获取枚举器的GetEnumerator方法,并允许循环
  • < em > ICollection < / em >包含额外的方法:Add, Remove, contains, Count, CopyTo
  • < em > ICollection < / em >继承自IEnumerable
  • 使用ICollection,您可以使用添加/删除等方法修改集合。你不能自由地对IEnumerable做同样的事情。

简单的程序:

using System;
using System.Collections;
using System.Collections.Generic;


namespace StackDemo
{
class Program
{
static void Main(string[] args)
{
List<Person> persons = new List<Person>();
persons.Add(new Person("John",30));
persons.Add(new Person("Jack", 27));


ICollection<Person> personCollection = persons;
IEnumerable<Person> personEnumeration = persons;


// IEnumeration
// IEnumration Contains only GetEnumerator method to get Enumerator and make a looping
foreach (Person p in personEnumeration)
{
Console.WriteLine("Name:{0}, Age:{1}", p.Name, p.Age);
}


// ICollection
// ICollection Add/Remove/Contains/Count/CopyTo
// ICollection is inherited from IEnumerable
personCollection.Add(new Person("Tim", 10));


foreach (Person p in personCollection)
{
Console.WriteLine("Name:{0}, Age:{1}", p.Name, p.Age);
}
Console.ReadLine();


}
}


class Person
{
public string Name { get; set; }
public int Age { get; set; }
public Person(string name, int age)
{
this.Name = name;
this.Age = age;
}
}
}

导航属性通常被定义为虚拟的,这样它们就可以利用某些实体框架的功能,比如延迟加载。

如果一个导航属性可以保存多个实体(如在多对多或一对多关系中),那么它的类型必须是一个列表,其中可以添加、删除和更新条目,例如ICollection。

https://www.asp.net/mvc/overview/getting-started/getting-started-with-ef-using-mvc/creating-an-entity-framework-data-model-for-an-asp-net-mvc-application

让我们试着跳出逻辑思维的框框,清楚地理解你问题中的这三个接口:

当某个实例的类实现了System.Collection.IEnumerable接口时,简单地说,我们可以说这个实例既是可枚举的又是可迭代的,这意味着这个实例允许以某种方式在一个循环中遍历/获取/传递/遍历/遍历这个实例包含的所有项和元素。

这意味着也可以枚举此实例包含的所有项和元素。

每个实现System.Collection.IEnumerable接口的类都实现了GetEnumerator方法,该方法不接受参数并返回System.Collections.IEnumerator实例。

System.Collections.IEnumerator接口的实例的行为非常类似于c++的迭代器。

当某个实例的类实现了System.Collection.ICollection接口时,简单地说,我们可以说这个实例是一些东西的集合。

此接口的通用版本,即System.Collection.Generic。ICollection的信息更丰富,因为这个泛型接口显式地声明了集合中事物的类型。

这一切都是合理的,合理的,合乎逻辑的,而且System.Collections.ICollection接口继承自System.Collections.IEnumerable接口,因为理论上每个集合都是可枚举的和可迭代的,理论上可以遍历每个集合中的所有项和元素。

icollection接口表示一个可变的有限动态集合,这意味着现有的项可以从集合中删除,新项可以添加到同一个集合中。

这就解释了为什么System.Collections.ICollection接口有“添加”和“删除”方法。

因为System.Collections.ICollection接口的实例是有限的集合,所以“有限”这个词意味着这个接口的每个集合总是有有限数量的项和元素。

System.Collections.ICollection接口的属性Count应该返回这个数字。

System.Collections.IEnumerable接口不具有System.Collections.ICollection接口所具有的这些方法和属性,因为System.Collections.IEnumerable接口具有System.Collections.ICollection接口所具有的这些方法和属性没有任何意义。

逻辑还说,不是每个实例都是可枚举的和可迭代的 一个集合,不一定是可变的。

我说的可更改,是指不要马上认为你可以从可枚举和可迭代的东西中添加或删除一些东西。

如果我创建了一些有限的素数序列,例如,这确实有限的素数的个数是System.Collections.IEnumerable接口的一个实例,因为现在我可以对所有的质数有限序列在一个循环,做任何我想做的与他们每个人,就像每个人打印到控制台窗口或屏幕,但这有限的素数序列不是System.Collections.ICollection接口的一个实例,因为把合数加到这个有限的素数序列中是没有意义的。

另外,您还希望在下一个迭代中获得下一个与当前迭代中的当前素数最接近的大素数,如果这样的话,您也不想从这个有限的素数序列中删除现有的素数。

此外,您可能还想在system . collections . ienumerable接口的GetEnumerator方法中使用、编码和编写“yield return”来生成质数,并且不在内存堆上分配任何东西,然后让垃圾收集器(GC)从堆中释放和释放这些内存,因为这显然是浪费操作系统内存并降低性能。

当调用System.Collections.ICollection接口的方法和属性时,应该在堆上进行动态内存分配和回收,而不是在调用System.Collections.IEnumerable接口的方法和属性时(尽管System.Collections.IEnumerable接口只有1个方法和0个属性)。

根据其他人在Stack Overflow网页中所说的,System.Collections.IList接口只是代表一个orderable集合,这解释了为什么System.Collections.IList接口的方法与System.Collections.ICollection接口的方法不同。

简而言之,System.Collections.ICollection接口并不暗示它的实例是可排序的,但System.Collections.IList接口暗示了这一点。

理论上有序集是无序集的特殊情况。

这也解释了为什么System.Collections.IList接口继承了System.Collections.ICollection接口。