Why use IList or List?

I know that there were a lot of messages on it, but it still confuses me why you should go through an interface like IList and return an interface like IList instead of a specific list.

I read a lot of posts about how this makes it easier to change the implementation later, but I just don't quite understand how this works.

Tell me if I have this method

public class SomeClass { public bool IsChecked { get; set; } } public void LogAllChecked(IList<SomeClass> someClasses) { foreach (var s in someClasses) { if (s.IsChecked) { // log } } } 

I'm not sure how in the future using IList will help me.

How about if I'm already in a method? Should I use IList?

 public void LogAllChecked(IList<SomeClass> someClasses) { //why not List<string> myStrings = new List<string>() IList<string> myStrings = new List<string>(); foreach (var s in someClasses) { if (s.IsChecked) { myStrings.Add(s.IsChecked.ToString()); } } } 

What can I use to use IList now?

 public IList<int> onlySomeInts(IList<int> myInts) { IList<int> store = new List<int>(); foreach (var i in myInts) { if (i % 2 == 0) { store.Add(i); } } return store; } 

how about now? Is there some new int list implementation that I will need to change?

So basically I need to see some actual code examples, as if I, where to use IList, would solve some kind of problem by simply taking List into everything.

From my reading, I think that I could use IEnumberable instead of IList, because I am just sorting through things.

Edit So I played with some of my methods on how to do this. I'm still not sure about the return type (if I have to make it more specific or an interface)

  public class CardFrmVm { public IList<TravelFeaturesVm> TravelFeaturesVm { get; set; } public IList<WarrantyFeaturesVm> WarrantyFeaturesVm { get; set; } public CardFrmVm() { WarrantyFeaturesVm = new List<WarrantyFeaturesVm>(); TravelFeaturesVm = new List<TravelFeaturesVm>(); } } public class WarrantyFeaturesVm : AvailableFeatureVm { } public class TravelFeaturesVm : AvailableFeatureVm { } public class AvailableFeatureVm { public Guid FeatureId { get; set; } public bool HasFeature { get; set; } public string Name { get; set; } } private IList<AvailableFeature> FillAvailableFeatures(IEnumerable<AvailableFeatureVm> avaliableFeaturesVm) { List<AvailableFeature> availableFeatures = new List<AvailableFeature>(); foreach (var f in avaliableFeaturesVm) { if (f.HasFeature) { // nhibernate call to Load<>() AvailableFeature availableFeature = featureService.LoadAvaliableFeatureById(f.FeatureId); availableFeatures.Add(availableFeature); } } return availableFeatures; } 

So, I'm returning an IList right now for the simple fact that I will add this to my domain model, which has a property like this

 public virtual IList<AvailableFeature> AvailableFeatures { get; set; } 

the above is IList itself, as that is what seems like a standard for use with nhibernate. Otherwise, I could return IEnumberable back, but not sure. Still can't understand what the user needs 100% (which is when concrete return takes precedence).

Edit 2

I also thought about what would happen if I wanted to do a pass by reference in my method?

 private void FillAvailableFeatures(IEnumerable<AvailableFeatureVm> avaliableFeaturesVm, IList<AvailableFeature> toFill) { foreach (var f in avaliableFeaturesVm) { if (f.HasFeature) { // nhibernate call to Load<>() AvailableFeature availableFeature = featureService.LoadAvaliableFeatureById(f.FeatureId); toFill.Add(availableFeature); } } } 

Am I having problems with this? So how can they not go into an array (which has a fixed size)? Would it be better perhaps for a specific list?

+72
c #
Jan 03 '12 at 19:30
source share
10 answers

There are three questions: what type should I use for a formal parameter? What should I use for a local variable? and what should i use for return type?

Formal parameters:

The principle here does not require more than you need . IEnumerable<T> reports: "I need to get the elements of this sequence from beginning to end." IList<T> reports: "I need to get and set the elements of this sequence in random order." List<T> says: "I need to get and set the elements of this sequence in random order, and I only accept lists, I don't accept arrays."

By asking for more than you need, you (1) force the caller to do unnecessary work to satisfy your unnecessary requirements and (2) tell the reader a lie. Just ask what you are going to use. Thus, if the caller has a sequence, they do not need to call ToList on it to satisfy your requirement.

Local variables:

Use whatever you want. This is your method. You are the only one who sees the internal details of the method implementation.

Return Type:

The same principle as before, the reverse. Offer the minimum minimum level that your caller requires. If the caller only requires an enumeration of the sequence, specify only IEnumerable<T> .

+130
Jan 03 '12 at 19:48
source share

The most practical reason I've ever seen was given by Jeffrey Richter in the CLR via C #.

A sample is to use the lowest class or interface for your arguments and return the most appropriate class or interface for your return types. This gives your subscribers more flexibility in passing types to your methods and most of the possibilities for reusing return values.

For example, the following method

 public void PrintTypes(IEnumerable items) { foreach(var item in items) Console.WriteLine(item.GetType().FullName); } 

allows you to call a method a transmission of any type that can be thrown into an enumerated one. If you were more specific

 public void PrintTypes(List items) 

then, say, if you have an array and you want to print your type names on the console, you first need to create a new list and populate it with your types. And if you used a general implementation, you could only use a method that works for any object only with objects of a certain type.

Speaking of return types, the more specific you are, the more flexible the subscribers can be with him.

 public List<string> GetNames() 

you can use this return type to repeat names

 foreach(var name in GetNames()) 

or you can index directly to the collection

 Console.WriteLine(GetNames()[0]) 

While if you return to a less defined type

 public IEnumerable GetNames() 

you will need to massage the return type to get the first value

 Console.WriteLine(GetNames().OfType<string>().First()); 
+27
Jan 3 2018-12-12T00:
source share

IEnumerable<T> allows you to iterate through the collection. ICollection<T> based on this, and also allows you to add and remove elements. IList<T> also allows you to access and change them at a specific index. By identifying the one you expect your consumer to work with, you can change your implementation. List<T> implements all three of these interfaces.

If you find your property as List<T> or even IList<T> when all you want your consumer to have is the ability to iterate through the collection. Then they may depend on the fact that they can change the list. Then later, if you decide to convert the actual data store from List<T> to Dictionary<T,U> and set the dictionary keys as the actual value for the property (I should have done just that before). Then consumers who expect their changes to be reflected within your class will no longer have that opportunity. This is a big problem! If you find List<T> as IEnumerable<T> , you can confidently predict that your collection will not change externally. This is one of the possibilities of exposing List<T> like any of the above interfaces.

This level of abstraction goes in the other direction when it belongs to the parameters of the method. When you pass your list to a method that accepts IEnumerable<T> , you can be sure that your list will not be modified. When you are the person implementing this method and you say you accept IEnumerable<T> because all you have to do is iterate over this list. Then, the person calling the method can call it any type of data that can be listed. This allows you to use your code in an unexpected but perfectly acceptable way.

It follows that the implementation of your method can represent its local variables as you wish. Implementation details are not displayed. By giving you the ability to change your code to something better without affecting the people calling your code.

You cannot predict the future. Assuming that the property type will always be useful, since List<T> immediately limits your ability to adapt to the unexpected expectations of your code. Yes, you can never change this data type from List<T> , but you can be sure if you need to. Your code is ready for this.

+9
Jan 04 2018-12-12T00:
source share

Short answer:

You pass in an interface so that regardless of the specific implementation of that interface, your code will support it.

If you use a specific implementation of a list, another implementation of the same list will not be supported by your code.

Read a little about inheritance and polymorphism .

+8
Jan 03 '12 at 19:38
source share

Here's an example: I had a project where our lists became very large, and the resulting fragmentation of the heap of a large object worsened performance. We replaced List with LinkedList. LinkedList does not contain an array, so suddenly we hardly used a bunch of large objects.

Basically, we used lists as IEnumerable<T> , so no further changes were required. (And yes, I would recommend declaring links as IEnumerable if all you do is list them.) In several places, we need a list pointer, so we wrote an inefficient IList<T> wrapper around linked lists. We need a list pointer infrequently, so inefficiency was not a problem. If that were the case, we could provide some other implementation of IList, perhaps as a set of small arrays that would be more efficiently indexable, and also avoid large objects.

In the end, you may need to replace the implementation for any reason; performance is just one opportunity. Regardless of the reason, using the least derived type will reduce the need for changes in your code when a certain type of runtime of your objects changes.

+4
Jan 03 2018-12-12T00:
source share

Inside the method, you should use var instead of IList or List . When your data source changes based on the method, your onlySomeInts method onlySomeInts persist.

The reason for using IList instead of List as parameters is that many things implement IList (List and [] as two examples), but only one thing implements List . It is more flexible for coding an interface.

If you are simply listing the values, you should use IEnumerable . Each data type that can contain more than one value implements IEnumerable (or should) and makes your method extremely flexible.

+3
Jan 03 2018-12-12T00:
source share

Using IList instead of List, greatly simplify test cases. This allows you to use the Mocking library to transfer and return data.

Another common reason for using interfaces is to disclose the minimum amount of knowledge needed by an object user.

Consider the (far-fetched) case where I have a data object that implements IList.

 public class MyDataObject : IList<int> { public void Method1() { ... } // etc } 

Your functions above only care about being able to iterate over the list. Ideally, they don’t need to know who implements this list or how they implement it.

In your example, IEnumerable is the best choice you thought.

+1
Jan 03 '12 at 19:45
source share

It is always a good idea to reduce the dependencies between your code as much as possible.

With this in mind, it makes sense to pass types with the least amount of external dependencies and return them. However, this may be different depending on the visibility of your methods and their signature.

If your methods are part of an interface, methods must be defined using the types available for that interface. Concrete types will probably not be available for interfaces, so they will have to return non-concrete types. You would like to do this if you were to create a framework, for example.

However, if you are not writing a framework, it may be useful to pass a parameter with weak possible types (i.e. base classes, interfaces, or even delegates) and return specific types. This gives the caller the ability to do as much as possible with the returned object, even if it is displayed as an interface. However, this makes the method more fragile, since any change in the type of the returned object may violate the calling code. In practice, although this is usually not a serious problem.

+1
Jan 03 '12 at 20:13
source share

You accept the interface as a parameter to the method because it allows the caller to pass various specific types as arguments. Given your LogAllChecked method method, the someClasses parameter can be of different types, and for the person writing the method, everything can be equivalent (i.e. you will write the same code regardless of the parameter type). But for the person calling the method, it can make a huge difference - if they have an array and you request a list, they must change the array to a list or vv whenever you call a method, losing time both from the programmer and from the performance Pov.

If you return an interface or a specific type, it depends on what you want your subscribers to do with the object you created - this is a solution for designing an API, and there is no hard and fast rule. You need to weigh their ability to fully use the object against their ability to easily use part of the functionality of the objects (and, of course, whether you want them to fully use the object). For example, if you return IEnumerable, then you restrict them to iteration - they cannot add or remove elements from your object, they can only act against objects. If you need to expose the collection outside the class, but do not want the caller to change the collection, this is one way to execute it. On the other hand, if you are returning an empty collection that you expect / want to fill, then IEnumerable is unsuitable.

+1
Jan 04 2018-12-12T00:
source share

Here is my answer in this .NET 4.5+ world.

Use IList <T> and IReadonlyList <T> ,
does not exist.

IList <T> looks so consistent with IReadonlyList <T>

  • Use IEnumerable <T> for minimal impact (property) or requirement (parameter) if foreach is the only way to use it.
  • Use IReadonlyList <T> if you also need to set / use Count and [] indexer.
  • Use IList <T> if you also allow subscribers to add / update / delete items

because List <T> implements IReadonlyList <T>, it does not require any explicit casting.

Class Example:

 // manipulate the list within the class private List<int> _numbers; // callers can add/update/remove elements, but cannot reassign a new list to this property public IList<int> Numbers { get { return _numbers; } } // callers can use: .Count and .ReadonlyNumbers[idx], but cannot add/update/remove elements public IReadOnlyList<int> ReadonlyNumbers { get { return _numbers; } } 
0
Mar 03 '17 at 21:51
source share



All Articles