Why use ICollection and not IEnumerable or List<T> on many-many/one-many relationships?
Asked Answered
O

10

449

I see this a lot in tutorials, with navigation properties as ICollection<T>.

Is this a mandatory requirement for Entity Framework? Can I use IEnumerable?

What's the main purpose of using ICollection instead of IEnumerable or even List<T>?

Obligate answered 11/4, 2012 at 20:14 Comment(1)
So the answer is, it is NOT mandatory to use ICollection. and I can use IEnumerable if iteration is al I want. right?Tollefson
S
515

Usually what you choose will depend on which methods you need access to. In general - IEnumerable<> (MSDN: http://msdn.microsoft.com/en-us/library/system.collections.ienumerable.aspx) for a list of objects that only needs to be iterated through, ICollection<> (MSDN: http://msdn.microsoft.com/en-us/library/92t2ye13.aspx) for a list of objects that needs to be iterated through and modified, List<> for a list of objects that needs to be iterated through, modified, sorted, etc (See here for a full list: http://msdn.microsoft.com/en-us/library/6sh2ey19.aspx).

From a more specific standpoint, lazy loading comes in to play with choosing the type. By default, navigation properties in Entity Framework come with change tracking and are proxies. In order for the dynamic proxy to be created as a navigation property, the virtual type must implement ICollection.

A navigation property that represents the "many" end of a relationship must return a type that implements ICollection, where T is the type of the object at the other end of the relationship. -Requirements for Creating POCO ProxiesMSDN

More information on Defining and Managing RelationshipsMSDN

Stunk answered 11/4, 2012 at 20:20 Comment(12)
so, with that, List should be a lot better, yeah?Obligate
@JanCarloViray - I tend to use List a lot. Although it has the most overhead it provides the most functionality.Stunk
Lists are defined more by their indexers than by the ability to sort them (having an integer indexer makes it easy to sort something, but it's not a requirement).Uttasta
@Uttasta - Yes, they are available to be indexed through ListName[i] which can be very handy. The sort is built in.Stunk
The sort is built in to the List<T> type, true, but that's more of an "extra". For example, the IList<T> interface doesn't have a Sort() method.Uttasta
With regard to your edit, restricting a property to an interface type is not about memory but about encapsulation. Consider: private IEnumerable<int> _integers = new List<int> { 1, 2, 3 }; uses the same memory as private List<int> _integers = new List<int> { 1, 2, 3 };Uttasta
@Uttasta - I am going to have to disagree with you there. Your example is flawed in that you are wrapping a List with an IEnumberable which is rather pointless. Just leave it as a List, you already took the hit! The point I was making of a small footprint in memory is that most IEnumberable implement a deferred execution (your example just explicitly makes that execution to be at the time of definition).Stunk
@TravisJ I am not wrapping a list in an IEnumerable, I am referencing a list as an IEnumerable. Replace IEnumerable<T> in my example with ICollection<T> (which means we're no longer confused by the possibility of using linq queries). How does that use any less memory than List<T>?Uttasta
@Uttasta - I am not sure there is much difference between Collection and List. However, I believe there is a difference between those two and IEnumerable. This difference lies in the iterator. In the more complex collections (List and Collection) I believe they use a reference type whereas IEnumberable uses a mutable struct (to increase performance).Stunk
let us continue this discussion in chatUttasta
@TravisJ: List<T> has a GetEnumerator() method, separate from its implementation of IEnumerable<T>, which returns a mutable structure type List<T>.Enumerator. In most contexts, that type will yield slightly better performance than would a standalone heap object. Compilers which duck-type enumerators (as both C# and vb.net do) can take advantage of this when generating foreach code. If the List<T> is cast to IEnumrable<T> before the foreach, the IEnumerable<T>.GetEnumerator() method will return a heap-allocated object, rendering the optimization impossible.Supertonic
IEnumarable is not thread safe either so by using it next to async methods is somekind of violation on it its own. The problem is that every thread that iterates on IEnumarable executes the backing query and if the context is closed on the main thread, and already enumerated object in another thread, fails to enumerate in this thread. ICollection holds your nongeneric data enumerated, so after Linq-to-SQL has executed there is no point in passing IEnumarble up the Layers any more. IMHOSitula
U
117

ICollection<T> is used because the IEnumerable<T> interface provides no way of adding items, removing items, or otherwise modifying the collection.

Unfriendly answered 11/4, 2012 at 20:16 Comment(3)
what about comparing against List<T>?Obligate
List<T> implements ICollection<T>.Huzzah
The non-generic ICollection doesn't allow any way to add items, but it's still a useful adjunct to IEnumerable<T> because it provides a Count member which is typically much faster than enumerating everything. Note that if an IList<Cat> or ICollection<Cat> is passed to code expecting an IEnumerable<Animal>, the Count() extension method will be fast if it implements the non-generic ICollection, but not if it only implements the generic interfaces since a typical ICollection<Cat> will not implement ICollection<Animal>.Supertonic
U
71

Responding to your question about List<T>:

List<T> is a class; specifying an interface allows more flexibility of implementation. A better question is "why not IList<T>?"

To answer that question, consider what IList<T> adds to ICollection<T>: integer indexing, which means the items have some arbitrary order, and can be retrieved by reference to that order. This is probably not meaningful in most cases, since items probably need to be ordered differently in different contexts.

Uttasta answered 11/4, 2012 at 20:35 Comment(0)
D
41

There are some basics difference between ICollection and IEnumerable

  • IEnumerable - contains only GetEnumerator method to get Enumerator and allows looping
  • ICollection contains additional methods: Add, Remove, Contains, Count, CopyTo
  • ICollection is inherited from IEnumerable
  • With ICollection you can modify the collection by using the methods like add/remove. You don't have the liberty to do the same with IEnumerable.

Simple Program:

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;
        }
    }
}
Dallas answered 2/11, 2016 at 11:56 Comment(0)
M
22

I remember it this way:

  1. IEnumerable has one method GetEnumerator() which allows one to read through the values in a collection but not write to it. Most of the complexity of using the enumerator is taken care of for us by the for each statement in C#. IEnumerable has one property: Current, which returns the current element.

  2. ICollection implements IEnumerable and adds few additional properties the most use of which is Count. The generic version of ICollection implements the Add() and Remove() methods.

  3. IList implements both IEnumerable and ICollection, and add the integer indexing access to items (which is not usually required, as ordering is done in database).

Multitude answered 15/3, 2015 at 13:57 Comment(2)
Based on what you wrote ICollection and IList are the same. Please add what is added to IList that does not exist in ICollection.Waikiki
ICollection VS IList, IList- only interface in the System.Collection that contains all functionality of IEnumerable and ICollection and additional functionality. IList has Insert and Remove methods. Both the methods accept index in their parameter. So, it supports index based operations over collection.Bennington
M
8

The basic idea of using ICollection is a provide an interface to readonly-access to some finite amount of data. In fact you have a ICollection.Count property. IEnumerable is more suitable for some chain of the data where you read till some logical point, some condition esplicitly specified by consumer or till the end of the enumeration.

Marjana answered 11/4, 2012 at 20:18 Comment(1)
TIL that ICollection is read-only while ICollection<T> is not.Buyse
E
2

What I have done in the past is declare my inner class collections using IList<Class>, ICollection<Class>or IEnumerable<Class> (if static list) depending on whether or not I will have to do any number of the following in a method in my repository: enumerate, sort/order or modify. When I just need to enumerate (and maybe sort) over objects then I create a temp List<Class>to work with the collection within an IEnumerable method. I think this practice would only be effective if the collection is relatively small, but it may be good practice in general, idk. Please correct me if there is evidence as to why this would not good practice.

Eberhard answered 24/1, 2014 at 18:18 Comment(0)
D
2

Navigation properties are typically defined as virtual so that they can take advantage of certain Entity Framework functionality such as lazy loading.

If a navigation property can hold multiple entities (as in many-to-many or one-to-many relationships), its type must be a list in which entries can be added, deleted, and updated, such as 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

Decretive answered 27/12, 2016 at 0:33 Comment(0)
M
2

Lets try thinking outside of the box with/by logic and understand clearly these three interfaces in your question:

When the class of some instance implements the System.Collection.IEnumerable interface then, in simple words, we can say that this instance is both enumerable and iterable, which means that this instance allows somehow in a single loop to go/get/pass/traverse/iterate over/through all the items and elements that this instance contains.

This means that this is also possible to enumerate all the items and elements that this instance contains.

Every class that implements the System.Collection.IEnumerable interface also implements the GetEnumerator method that takes no arguments and returns an System.Collections.IEnumerator instance.

Instances of System.Collections.IEnumerator interface behaves very similar to C++ iterators.

When the class of some instance implements the System.Collection.ICollection interface then, in simple words, we can say that this instance is some collection of things.

The generic version of this interface, i.e. System.Collection.Generic.ICollection, is more informative because this generic interface explicitly states what is the type of the things in the collection.

This is all reasonable, rational, logical and makes sense that System.Collections.ICollection interface inherits from System.Collections.IEnumerable interface, because theoretically every collection is also both enumerable and iterable and this is theoretically possible to go over all the items and elements in every collection.

System.Collections.ICollection interface represents a finite dynamic collection that are changeable, which means that exist items can be removed from the collection and new items can be added to the same collection.

This explains why System.Collections.ICollection interface has the "Add" and "Remove" methods.

Because that instances of System.Collections.ICollection interface are finite collections then the word "finite" implies that every collection of this interface always has a finite number of items and elements in it.

The property Count of System.Collections.ICollection interface supposes to return this number.

System.Collections.IEnumerable interface does not have these methods and properties that System.Collections.ICollection interface has, because it does not make any sense that System.Collections.IEnumerable will have these methods and properties that System.Collections.ICollection interface has.

The logic also says that every instance that is both enumerable and iterable is not necessarily a collection and not necessarily changeable.

When I say changeable, I mean that don't immediately think that you can add or remove something from something that is both enumerable and iterable.

If I just created some finite sequence of prime numbers, for example, this finite sequence of prime numbers is indeed an instance of System.Collections.IEnumerable interface, because now I can go over all the prime numbers in this finite sequence in a single loop and do whatever I want to do with each of them, like printing each of them to the console window or screen, but this finite sequence of prime numbers is not an instance of System.Collections.ICollection interface, because this is not making sense to add composite numbers to this finite sequence of prime numbers.

Also you want in the next iteration to get the next closest larger prime number to the current prime number in the current iteration, if so you also don't want to remove exist prime numbers from this finite sequence of prime numbers.

Also you probably want to use, code and write "yield return" in the GetEnumerator method of the System.Collections.IEnumerable interface to produce the prime numbers and not allocating anything on the memory heap and then task the Garbage Collector (GC) to both deallocate and free this memory from the heap, because this is obviously both waste of operating system memory and decreases performance.

Dynamic memory allocation and deallocation on the heap should be done when invoking the methods and properties of System.Collections.ICollection interface, but not when invoking the methods and properties of System.Collections.IEnumerable interface (although System.Collections.IEnumerable interface has only 1 method and 0 properties).

According to what others said in this Stack Overflow webpage, System.Collections.IList interface simply represents an orderable collection and this explains why the methods of System.Collections.IList interface work with indexes in contrast to these of System.Collections.ICollection interface.

In short System.Collections.ICollection interface does not imply that an instance of it is orderable, but System.Collections.IList interface does imply that.

Theoretically ordered set is special case of unordered set.

This also makes sense and explains why System.Collections.IList interface inherits System.Collections.ICollection interface.

Malatya answered 10/8, 2019 at 18:10 Comment(0)
G
-2

Googling brought me here, and I haven't seen anyone mention this yet. I just got bamboozled by an API that looks similar to the following:

void Load(object key)
void Load(IEnumerable keys)

Passing in a string selects the IEnumerable overload, which is not what I (nor the API authors) intended. If the IEnumerable would have been an ICollection instead, then the object overload would have been selected for strings, which is the intention.

Just something to think about when trying to express intent in APIs. I'd guess it's more common than not for people to use IEnumerable even when their intent is to treat the type as a collection and NOT include other types of enumerables (like string).

Gas answered 13/3, 2023 at 3:34 Comment(0)

© 2022 - 2024 — McMap. All rights reserved.