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

时间:2012-04-11 20:14:03

标签: c# entity-framework code-first icollection

我在教程中看到了很多,导航属性为ICollection<T>

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

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

9 个答案:

答案 0 :(得分:398)

通常您选择的内容取决于您需要访问的方法。通常 - 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是关系另一端的对象类型。 - Requirements for Creating POCO ProxiesMSDN

More information on Defining and Managing RelationshipsMSDN

答案 1 :(得分:78)

使用

ICollection<T>是因为IEnumerable<T>接口无法添加项目,删除项目或以其他方式修改集合。

答案 2 :(得分:56)

回答有关List<T>的问题:

List<T>是一个班级;指定接口允许更灵活的实现。一个更好的问题是“为什么不IList<T>?”

要回答这个问题,请考虑IList<T>添加到ICollection<T>的内容:整数索引,这意味着项目具有一些任意顺序,并且可以通过引用该订单来检索。在大多数情况下,这可能没有意义,因为可能需要在不同的上下文中对项目进行不同的排序。

答案 3 :(得分:17)

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

  • IEnumerable - 仅包含GetEnumerator方法以获取Enumerator并进行循环
  • ICollection 包含以下方法 - 添加/删除/包含/计数/复制
  • ICollection 继承自IEnumerable
  • 使用ICollection,您可以使用add / remove等方法修改集合,您无权使用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;
        }
    }
}

答案 4 :(得分:8)

使用ICollection的基本思想是提供一个接口来只读访问一些有限数量的数据。实际上你有一个ICollection.Count属性。 IEnumerable更适合于您阅读的某些数据链,直到某个逻辑点,某些条件由消费者明确指定或直到枚举结束。

答案 5 :(得分:8)

我记得这样:

  1. IEnumerable有一个方法GetEnumerator(),它允许读取集合中的值但不写入它。使用枚举器的大部分复杂性由C#中的每个语句为我们处理。 IEnumerable有一个属性:Current,它返回当前元素。

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

  3. IList实现IEnumerable和ICollection,并添加对项的整数索引访问(通常不需要,因为在数据库中完成排序)。

答案 6 :(得分:2)

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

答案 7 :(得分:2)

导航属性通常定义为虚拟,以便他们可以利用某些实体框架功能,例如延迟加载。

如果导航属性可以包含多个实体(如多对多或一对多关系),则其类型必须是可以添加,删除和更新条目的列表,例如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

答案 8 :(得分:0)

让我们尝试通过逻辑思维开箱即用,并清楚地理解问题中的这三个界面:

当某个实例的类实现System.Collection.IEnumerable接口时,用简单的话来说,我们可以说该实例既可枚举又可迭代,这意味着该实例允许以某种方式在单个循环中进行/获取/ pass / traverse /迭代该实例包含的所有项目和元素。

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

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

System.Collections.IEnumerator接口的实例的行为与C ++迭代器非常相似。

当某个实例的类实现System.Collection.ICollection接口时,简单来说,我们可以说该实例是事物的某种集合。

此接口的通用版本(即System.Collection.Generic.ICollection)更具参考价值,因为此通用接口明确声明了集合中事物的类型。

这都是合理,合理,逻辑的,并且使System.Collections.ICollection接口继承自System.Collections.IEnumerable接口是有意义的,因为从理论上讲每个集合都是可枚举和可迭代的,并且理论上可以遍历所有每个集合中的项目和元素。

System.Collections.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接口的实例,因为这不是在此有限质数序列上增加复合数是很有意义的。

此外,您还希望在下一次迭代中获得当前迭代中当前质数的下一个最接近的较大质数,如果是这样,您也不想从此有限质数序列中删除存在的质数。 / p>

您还可能想在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接口。