Related
I realize that IEnumerable<T> has been discussed quite a bit on this forum.
When you make a call to something that returns an IEnumerable of any type of object, you are not getting back a known concrete implementation of anything. If you inspect the returned data in your debugger (or calling GetType() on the returned collection), its type is simply IEnumerable<MyClass> (unless of course we materialize it by calling ToList(), ToArray() et al).
I am also well aware that if we are creating a class that implements IEnumerable<T>, we have to implement the GetEnumerator() method ourselves.
But how does the framework/runtime handle this under the hood? For example, even in a trivial case like IEnumerable<int> numbers = Enumerable.Range(0, 10), we know that at some point during the execution chain, a concrete implementation of GetEnumerator() is being created and called, even though we aren't implementing that method ourselves.
Is Microsoft treating this as a marker interface? In other words - does the runtime, upon encountering/processing a method in our code that returns an IEnumerable, treat it as metadata and create/inject some kind of default implementation?
Technically speaking, marker interfaces are (arguably) a code smell, since according to OOP principles an interface is supposed to be a mere contract or template and should not actually do anything (except enforce that contract upon classes implementing it).
But in the case of IEnumerable<T>, there are clearly some things being done under the covers. If you examine the numbers variable in a debugger in my trivial example above, you will see that it contains actual, concrete, callable members which must have been implemented somewhere.
For example: if you call GetEnumerator() on any instance of an IEnumerable, what should happen (if MS was adhering to the strict definition of the OOP concept of interfaces) is some kind of exception (i.e., "Hey, I'm just an interface, I just declare that method as part of the contract, I don't actually contain an implementation of it and I have no idea what to do with it"). But instead, it gets called successfully.
What is really going on?
Something that returns an IEnumerable<T> is actually returning a class that implements that interface.
For instance take your example (Enumerable.Range), here is the source code
public static IEnumerable<int> Range(int start, int count)
{
long max = ((long)start) + count - 1;
if (count < 0 || max > int.MaxValue)
{
ThrowHelper.ThrowArgumentOutOfRangeException(ExceptionArgument.count);
}
if (count == 0)
{
return Empty<int>();
}
return new RangeIterator(start, count); <<<<=====
}
see https://source.dot.net/#System.Linq/System/Linq/Range.cs,fda9d378095a6464
following RangeIterator declaration stack
private sealed partial class RangeIterator : Iterator<int>
and
internal abstract class Iterator<TSource> : IEnumerable<TSource>, IEnumerator<TSource>
no magic, Enumerable.Range returns an instance of RangeIterator, which implements IEnumerable<T>
The point of interfaces is that the caller doesn't need to know anything about what's going on behind the interface, other than the returned object implements it. So the caller can call the interface methods knowing that they will work
reading the LINQ source is very informative, use that source browser its amazingly useful https://source.dot.net/
The answers to questions like this: List<T> or IList<T> always seem to agree that returning an interface is better than returning a concrete implementation of a collection. But I'm struggling with this. Instantiating an interface is impossible, so if your method is returning an interface, it's actually still returning a specific implementation. I was experimenting a bit with this by writing 2 small methods:
public static IList<int> ExposeArrayIList()
{
return new[] { 1, 2, 3 };
}
public static IList<int> ExposeListIList()
{
return new List<int> { 1, 2, 3 };
}
And use them in my test program:
static void Main(string[] args)
{
IList<int> arrayIList = ExposeArrayIList();
IList<int> listIList = ExposeListIList();
//Will give a runtime error
arrayIList.Add(10);
//Runs perfectly
listIList.Add(10);
}
In both cases when I try to add a new value, my compiler gives me no errors, but obviously the method which exposes my array as an IList<T> gives a runtime error when I try to add something to it.
So people who don't know what's happening in my method, and have to add values to it, are forced to first copy my IList to a List to be able to add values without risking errors. Of course they can do a typecheck to see if they're dealing with a List or an Array, but if they don't do that, and they want to add items to the collection they have no other choice to copy the IList to a List, even if it already is a List. Should an array never be exposed as IList?
Another concern of mine is based upon the accepted answer of the linked question (emphasis mine):
If you are exposing your class through a library that others will use, you generally want to expose it via interfaces rather than concrete implementations. This will help if you decide to change the implementation of your class later to use a different concrete class. In that case the users of your library won't need to update their code since the interface doesn't change.
If you are just using it internally, you may not care so much, and using List may be ok.
Imagine someone actually used my IList<T> they got from my ExposeListIlist() method just like that to add/remove values. Everything works fine. But now like the answer suggests, because returning an interface is more flexible I return an array instead of a List (no problem on my side!), then they're in for a treat...
TLDR:
1) Exposing an interface causes unnecessary casts? Does that not matter?
2) Sometimes if users of the library don't use a cast, their code can break when you change your method, even though the method remains perfectly fine.
I am probably overthinking this, but I don't get the general consensus that returning an interface is to be preferred over returning an implementation.
Maybe this is not directly answering your question, but in .NET 4.5+, I prefer to follow these rules when designing public or protected APIs:
do return IEnumerable<T>, if only enumeration is available;
do return IReadOnlyCollection<T> if both enumeration and items count are available;
do return IReadOnlyList<T>, if enumeration, items count and indexed access are available;
do return ICollection<T> if enumeration, items count and modification are available;
do return IList<T>, if enumeration, items count, indexed access and modification are available.
Last two options assume, that method must not return array as IList<T> implementation.
No, because the consumer should know what exactly IList is:
IList is a descendant of the ICollection interface and is the base
interface of all non-generic lists. IList implementations fall into
three categories: read-only, fixed-size, and variable-size. A
read-only IList cannot be modified. A fixed-size IList does not allow
the addition or removal of elements, but it allows the modification of
existing elements. A variable-size IList allows the addition, removal,
and modification of elements.
You can check for IList.IsFixedSize and IList.IsReadOnly and do what you want with it.
I think IList is an example of a fat interface and it should have been split into multiple smaller interfaces and it also violates Liskov substitution principle when you return an array as an IList.
Read more if you want to make decision about returning interface
UPDATE
Digging more and I found that IList<T> does not implement IList and IsReadOnly is accessible through base interface ICollection<T> but there is no IsFixedSize for IList<T>. Read more about why generic IList<> does not inherit non-generic IList?
As with all "interface versus implementation" question, you'll have to realise what exposing a public member means: it defines the public API of this class.
If you expose a List<T> as a member (field, property, method, ...), you tell the consumer of that member: the type obtained by accessing this method is a List<T>, or something derived of that.
Now if you expose an interface, you hide the "implementation detail" of your class using a concrete type. Of course you can't instantiate IList<T>, but you can use an Collection<T>, List<T>, derivations thereof or your own type implementing IList<T>.
The actual question is "Why does Array implement IList<T>", or "Why has the IList<T> interface so many members".
It also depends on what you want the consumers of that member to do. If you actually return an internal member through your Expose... member, you'll want to return a new List<T>(internalMember) anyway, as otherwise the consumer can try and cast them to IList<T> and modify your internal member through that.
If you just expect consumers to iterate the results, expose IEnumerable<T> or IReadOnlyCollection<T> instead.
Be careful with blanket quotes that are taken out of context.
Returning an interface is better than returning a concrete implementation
This quote only makes sense if it's used in the context of the SOLID principles. There are 5 principles but for the purposes of this discussion we'll just talk about the last 3.
Dependency inversion principle
one should “Depend upon Abstractions. Do not depend upon concretions.”
In my opinion, this principle is the most difficult to understand. But if you look at the quote carefully it looks a lot like your original quote.
Depend on interfaces (abstractions). Do no depend on concrete implementations (concretions).
This is still a little confusing but if we start applying the other principles together it starts to make a lot more sense.
Liskov substitution principle
“objects in a program should be replaceable with instances of their subtypes without altering the correctness of that program.”
As you pointed out, returning an Array is clearly different behavior to returning a List<T> even though they both implement IList<T>. This is most certainly a violation of LSP.
The important thing to realize is that interfaces are about the consumer. If you're returning an interface, you've created a contract that any methods or properties on that interface can be used without changing the behavior of the program.
Interface segregation principle
“many client-specific interfaces are better than one general-purpose interface.”
If you're returning an interface, you should return the most client specific interface your implementation supports. In other words, if you're not expecting the client to call the Add method you shouldn't return an interface with an Add method on it.
Unfortunately, the interfaces in the .NET framework (particularly the early versions) are not always ideal client specific interfaces. Although as #Dennis pointed out in his answer, there are a lot more choices in .NET 4.5+.
Returning an interface is not necessarily better than returning a concrete implementation of a collection. You should always have a good reason to use an interface instead of a concrete type. In your example it seems pointless to do so.
Valid reasons to use an interface could be:
You do not know what the implementation of the methods returning the interface will look like and there may be many, developed over time. It may be other people writing them, from other companies. So you just want to agree on the bare necessities and leave it up to them how to implement the functionality.
You want to expose some common functionality independent from your class hierarchy in a type-safe way. Objects of different base types that should offer the same methods would implement your interface.
One could argue that 1 and 2 are basically the same reason. They are two different scenarios that ultimately lead to the same need.
"It's a contract". If the contract is with yourself and your application is closed in both functionality and time, there is often no point in using an interface.
Closed. This question is opinion-based. It is not currently accepting answers.
Want to improve this question? Update the question so it can be answered with facts and citations by editing this post.
Closed 3 years ago.
Improve this question
Can anyone explain to me why I would want to use IList over List in C#?
Related question: Why is it considered bad to expose List<T>
If you are exposing your class through a library that others will use, you generally want to expose it via interfaces rather than concrete implementations. This will help if you decide to change the implementation of your class later to use a different concrete class. In that case the users of your library won't need to update their code since the interface doesn't change.
If you are just using it internally, you may not care so much, and using List<T> may be ok.
The less popular answer is programmers like to pretend their software is going to be re-used the world over, when infact the majority of projects will be maintained by a small amount of people and however nice interface-related soundbites are, you're deluding yourself.
Architecture Astronauts. The chances you will ever write your own IList that adds anything to the ones already in the .NET framework are so remote that it's theoretical jelly tots reserved for "best practices".
Obviously if you are being asked which you use in an interview, you say IList, smile, and both look pleased at yourselves for being so clever. Or for a public facing API, IList. Hopefully you get my point.
Interface is a promise (or a contract).
As it is always with the promises - smaller the better.
Some people say "always use IList<T> instead of List<T>".
They want you to change your method signatures from void Foo(List<T> input) to void Foo(IList<T> input).
These people are wrong.
It's more nuanced than that. If you are returning an IList<T> as part of the public interface to your library, you leave yourself interesting options to perhaps make a custom list in the future. You may not ever need that option, but it's an argument. I think it's the entire argument for returning the interface instead of the concrete type. It's worth mentioning, but in this case it has a serious flaw.
As a minor counterargument, you may find every single caller needs a List<T> anyway, and the calling code is littered with .ToList()
But far more importantly, if you are accepting an IList as a parameter you'd better be careful, because IList<T> and List<T> do not behave the same way. Despite the similarity in name, and despite sharing an interface they do not expose the same contract.
Suppose you have this method:
public Foo(List<int> a)
{
a.Add(someNumber);
}
A helpful colleague "refactors" the method to accept IList<int>.
Your code is now broken, because int[] implements IList<int>, but is of fixed size. The contract for ICollection<T> (the base of IList<T>) requires the code that uses it to check the IsReadOnly flag before attempting to add or remove items from the collection. The contract for List<T> does not.
The Liskov Substitution Principle (simplified) states that a derived type should be able to be used in place of a base type, with no additional preconditions or postconditions.
This feels like it breaks the Liskov substitution principle.
int[] array = new[] {1, 2, 3};
IList<int> ilist = array;
ilist.Add(4); // throws System.NotSupportedException
ilist.Insert(0, 0); // throws System.NotSupportedException
ilist.Remove(3); // throws System.NotSupportedException
ilist.RemoveAt(0); // throws System.NotSupportedException
But it doesn't. The answer to this is that the example used IList<T>/ICollection<T> wrong. If you use an ICollection<T> you need to check the IsReadOnly flag.
if (!ilist.IsReadOnly)
{
ilist.Add(4);
ilist.Insert(0, 0);
ilist.Remove(3);
ilist.RemoveAt(0);
}
else
{
// what were you planning to do if you were given a read only list anyway?
}
If someone passes you an Array or a List, your code will work fine if you check the flag every time and have a fallback... But really; who does that? Don't you know in advance if your method needs a list that can take additional members; don't you specify that in the method signature? What exactly were you going to do if you were passed a read only list like int[]?
You can substitute a List<T> into code that uses IList<T>/ICollection<T> correctly. You cannot guarantee that you can substitute an IList<T>/ICollection<T> into code that uses List<T>.
There's an appeal to the Single Responsibility Principle / Interface Segregation Principle in a lot of the arguments to use abstractions instead of concrete types - depend on the narrowest possible interface. In most cases, if you are using a List<T> and you think you could use a narrower interface instead - why not IEnumerable<T>? This is often a better fit if you don't need to add items. If you need to add to the collection, use the concrete type, List<T>.
For me IList<T> (and ICollection<T>) is the worst part of the .NET framework. IsReadOnly violates the principle of least surprise. A class, such as Array, which never allows adding, inserting or removing items should not implement an interface with Add, Insert and Remove methods. (see also https://softwareengineering.stackexchange.com/questions/306105/implementing-an-interface-when-you-dont-need-one-of-the-properties)
Is IList<T> a good fit for your organisation? If a colleague asks you to change a method signature to use IList<T> instead of List<T>, ask them how they'd add an element to an IList<T>. If they don't know about IsReadOnly (and most people don't), then don't use IList<T>. Ever.
Note that the IsReadOnly flag comes from ICollection<T>, and indicates whether items can be added or removed from the collection; but just to really confuse things, it does not indicate whether they can be replaced, which in the case of Arrays (which return IsReadOnlys == true) can be.
For more on IsReadOnly, see msdn definition of ICollection<T>.IsReadOnly
List<T> is a specific implementation of IList<T>, which is a container that can be addressed the same way as a linear array T[] using an integer index. When you specify IList<T> as the type of the method's argument, you only specify that you need certain capabilities of the container.
For example, the interface specification does not enforce a specific data structure to be used. The implementation of List<T> happens to the same performance for accessing, deleting and adding elements as a linear array. However, you could imagine an implementation that is backed by a linked list instead, for which adding elements to the end is cheaper (constant-time) but random-access much more expensive. (Note that the .NET LinkedList<T> does not implement IList<T>.)
This example also tells you that there may be situations when you need to specify the implementation, not the interface, in the argument list: In this example, whenever you require a particular access performance characteristic. This is usually guaranteed for a specific implementation of a container (List<T> documentation: "It implements the IList<T> generic interface using an array whose size is dynamically increased as required.").
Additionally, you might want to consider exposing the least functionality you need. For example. if you don't need to change the content of the list, you should probably consider using IEnumerable<T>, which IList<T> extends.
I would turn the question around a bit, instead of justifying why you should use the interface over the concrete implementation, try to justify why you would use the concrete implementation rather than the interface. If you can't justify it, use the interface.
IList<T> is an interface so you can inherit another class and still implement IList<T> while inheriting List<T> prevents you to do so.
For example if there is a class A and your class B inherits it then you can't use List<T>
class A : B, IList<T> { ... }
public void Foo(IList<Bar> list)
{
// Do Something with the list here.
}
In this case you could pass in any class which implements the IList<Bar> interface. If you used List<Bar> instead, only a List<Bar> instance could be passed in.
The IList<Bar> way is more loosely coupled than the List<Bar> way.
A principle of TDD and OOP generally is programming to an interface not an implementation.
In this specific case since you're essentially talking about a language construct, not a custom one it generally won't matter, but say for example that you found List didn't support something you needed. If you had used IList in the rest of the app you could extend List with your own custom class and still be able to pass that around without refactoring.
The cost to do this is minimal, why not save yourself the headache later? It's what the interface principle is all about.
The most important case for using interfaces over implementations is in the parameters to your API. If your API takes a List parameter, then anyone who uses it has to use List. If the parameter type is IList, then the caller has much more freedom, and can use classes you never heard about, which may not even have existed when your code was written.
Supprising that none of these List vs IList questions (or answers) mentions the signature difference. (Which is why I searched for this question on SO!)
So here's the methods contained by List that are not found in IList, at least as of .NET 4.5 (circa 2015)
AddRange
AsReadOnly
BinarySearch
Capacity
ConvertAll
Exists
Find
FindAll
FindIndex
FindLast
FindLastIndex
ForEach
GetRange
InsertRange
LastIndexOf
RemoveAll
RemoveRange
Reverse
Sort
ToArray
TrimExcess
TrueForAll
What if .NET 5.0 replaces System.Collections.Generic.List<T> to System.Collection.Generics.LinearList<T>. .NET always owns the name List<T> but they guarantee that IList<T> is a contract. So IMHO we (atleast I) are not supposed to use someone's name (though it is .NET in this case) and get into trouble later.
In case of using IList<T>, the caller is always guareented things to work, and the implementer is free to change the underlying collection to any alternative concrete implementation of IList
All concepts are basically stated in most of the answers above regarding why use interface over concrete implementations.
IList<T> defines those methods (not including extension methods)
IList<T> MSDN link
Add
Clear
Contains
CopyTo
GetEnumerator
IndexOf
Insert
Remove
RemoveAt
List<T> implements those nine methods (not including extension methods), on top of that it has about 41 public methods, which weighs in your consideration of which one to use in your application.
List<T> MSDN link
You would because defining an IList or an ICollection would open up for other implementations of your interfaces.
You might want to have an IOrderRepository that defines a collection of orders in either a IList or ICollection. You could then have different kinds of implementations to provide a list of orders as long as they conform to "rules" defined by your IList or ICollection.
IList<> is almost always preferable as per the other poster's advice, however note there is a bug in .NET 3.5 sp 1 when running an IList<> through more than one cycle of serialization / deserialization with the WCF DataContractSerializer.
There is now a SP to fix this bug : KB 971030
The interface ensures that you at least get the methods you are expecting; being aware of the definition of the interface ie. all abstract methods that are there to be implemented by any class inheriting the interface. so if some one makes a huge class of his own with several methods besides the ones he inherited from the interface for some addition functionality, and those are of no use to you, its better to use a reference to a subclass (in this case the interface) and assign the concrete class object to it.
additional advantage is that your code is safe from any changes to concrete class as you are subscribing to only few of the methods of concrete class and those are the ones that are going to be there as long as the concrete class inherits from the interface you are using. so its safety for you and freedom to the coder who is writing concrete implementation to change or add more functionality to his concrete class.
You can look at this argument from several angles including the one of a purely OO approach which says to program against an Interface not an implementation. With this thought, using IList follows the same principal as passing around and using Interfaces that you define from scratch. I also believe in the scalability and flexibility factors provided by an Interface in general. If a class implmenting IList<T> needs to be extended or changed, the consuming code does not have to change; it knows what the IList Interface contract adheres to. However using a concrete implementation and List<T> on a class that changes, could cause the calling code to need to be changed as well. This is because a class adhering to IList<T> guarantees a certain behavior that is not guaranteed by a concrete type using List<T>.
Also having the power to do something like modify the default implementation of List<T> on a class Implementing IList<T> for say the .Add, .Remove or any other IList method gives the developer a lot of flexibility and power, otherwise predefined by List<T>
Typically, a good approach is to use IList in your public facing API (when appropriate, and list semantics are needed), and then List internally to implement the API. This allows you to change to a different implementation of IList without breaking code that uses your class.
The class name List may be changed in next .net framework but the interface is never going to change as interface is contract.
Note that, if your API is only going to be used in foreach loops, etc, then you might want to consider just exposing IEnumerable instead.
I'm implementing a persistent collection - for the sake of argument, let's say it's a singly-linked list, of the style common in functional languages.
class MyList<T>
{
public T Head { get; }
public MyList<T> Tail { get; }
// other various stuff
// . . .
}
It seems natural to have this class implement ICollection<T>, since it can implement all the normal behavior one would expect of an ICollection<T>, at least in broad strokes. But there is a lot of mismatch between this class's behavior and ICollection<T>. For example, the signature of the Add() method
void Add(T item); // ICollection<T> version
assumes that the addition will be performed as a side-effect that mutates the collection. But this is a persistent data structure, so Add() should instead create a new list and return it.
MyList<T> Add(T item); // what we really want
It seems the best way to resolve this is to just create the version we want, and also generate a non-functional explicit implementation of the version defined in the interface.
void ICollection<T>.Add(T item) { throw new NotSupportedException(); }
public MyList<T> Add(T item) { return new MyList<T>(item, this); }
But I have a few concerns about that option:
Will this be confusing to users? I envision scenarios where someone is working with this class, and finds that calling Add() on it sometimes raises an exception, and sometimes runs but doesn't modify the list as would normally be expected for an ICollection, depending on the type information associated with the reference being used?
Following on (1), the implementation of ICollection<T>'s IsReadOnly should presumably return true. But that would seem to conflict with what is implied in other spots where Add() is being used with instances of the class.
Is (2) resolved in a non-confusing way by following the explicit implementation pattern again, with the new version returning false and the explicit implementation returning true? Or does this just make it even worse by falsely implying that MyList<T>'s Add() method is a mutator?
Or would it be better to forget trying to use the existing interface and just create a separate IPersistentCollection<T> interface that derives from IEnumerable<T> instead?
edit I changed the name of the class, and switched over to using ICollection. I wanted to focus on the object's behavior and how it relates to the interface. I just went with the cons list as a simple example. I appreciate the advice that if I were to implement a cons list I should try and come up with a less-confusing name and, should avoid implementing IList because that interface is intended for fast random access, but they are somewhat tangential issues.
What I intended to ask about is what others think about the tension between the semantics of read-only (or immutable) collections that are baked into the Framework, and persistent collections which implement equivalent behavior to what is described by the interface, only functionally rather than through mutating side effects.
Will implementing IList<T> be confusing?
Yes. Though there are situations in which an implementation of IList<T> throws -- say, when you are attempting to resize the list but its implementation is an array -- I would find it quite confusing to have an IList<T> that could be mutated in no way and did not have fast random access.
Should I implement a new IPersistentList<T>?
That depends on whether anyone will use it. Are consumers of your class likely to have a half-dozen different implementations of IPL<T> to choose from? I see no point in making an interface that is implemented by only one class; just use the class.
WPF's ItemsControl can get better performance if its ItemsSource is an IList<T> instead of an IEnumerable<T>.
But your persistent linked list will not have fast random access anyway.
It would make more sense to me to make a new IPersistentList<T> (or IImmutableList<T> since "persistent" sounds to me like the data is saved off somewhere.) interface since, really, it's different behavior than what is expected of an IList<T>. Classes that implement IList<T> should be mutable IMHO.
Oh, and of course, I'd avoid using the class name List<T> since it's already part of the framework.
Update: I appreciate all of the comments, which have essentially comprised unanimous opposition. While every objection raised was valid, I feel that the ultimate nail in the coffin was Ani's astute observation that, ultimately, even the one miniscule benefit that this idea ostensibly offered -- the elimination of boilerplate code -- was negated by the fact that the idea itself would require its own boilerplate code.
So yeah, consider me convinced: it would be a bad idea.
And just to sort of salvage my dignity somewhat: I might have played it up for argument's sake, but I was never really sold on this idea to begin with -- merely curious to hear what others had to say about it. Honest.
Before you dismiss this question as absurd, I ask you to consider the following:
IEnumerable<T> inherits from* IEnumerable, which means that any type that implements IEnumerable<T> generally must implement both IEnumerable<T>.GetEnumerator and (explicitly) IEnumerable.GetEnumerator. This basically amounts to boilerplate code.
You can foreach over any type that has a GetEnumerator method, as long as that method returns an object of some type with a MoveNext method and a Current property. So if your type defines one method with the signature public IEnumerator<T> GetEnumerator(), it's legal to enumerate over it using foreach.
Clearly, there is a lot of code out there that requires the IEnumerable<T> interface -- for instance, basically all of the LINQ extension methods. Luckily, to go from a type that you can foreach on to an IEnumerable<T> is trivial using the automatic iterator generation that C# supplies via the yield keyword.
So, putting this all together, I had this crazy idea: what if I just define my own interface that looks like this:
public interface IForEachable<T>
{
IEnumerator<T> GetEnumerator();
}
Then whenever I define a type that I want to be enumerable, I implement this interface instead of IEnumerable<T>, eliminating the need to implement two GetEnumerator methods (one explicit). For example:
class NaturalNumbers : IForEachable<int>
{
public IEnumerator<int> GetEnumerator()
{
int i = 1;
while (i < int.MaxValue)
{
yield return (i++);
}
}
// Notice how I don't have to define a method like
// IEnumerator IEnumerable.GetEnumerator().
}
Finally, in order to make this type compatible with code that does expect the IEnumerable<T> interface, I can just define an extension method to go from any IForEachable<T> to an IEnumerable<T> like so:
public static class ForEachableExtensions
{
public static IEnumerable<T> AsEnumerable<T>(this IForEachable<T> source)
{
foreach (T item in source)
{
yield return item;
}
}
}
It seems to me that doing this enables me to design types that are usable in every way as implementations of IEnumerable<T>, but without that pesky explicit IEnumerable.GetEnumerator implementation in each one.
For example:
var numbers = new NaturalNumbers();
// I can foreach myself...
foreach (int x in numbers)
{
if (x > 100)
break;
if (x % 2 != 0)
continue;
Console.WriteLine(x);
}
// Or I can treat this object as an IEnumerable<T> implementation
// if I want to...
var evenNumbers = from x in numbers.AsEnumerable()
where x % 2 == 0
select x;
foreach (int x in evenNumbers.TakeWhile(i => i <= 100))
{
Console.WriteLine(x);
}
What do you guys think of this idea? Am I missing some reason why this would be a mistake?
I realize it probably seems like an overly complex solution to what isn't that big of a deal to start with (I doubt anybody really cares that much about having to explicitly define the IEnumerable interface); but it just popped into my head and I'm not seeing any obvious problems that this approach would pose.
In general, if I can write a moderate amount of code once to save myself the trouble of having to write a small amount of code lots of times, to me, it's worth it.
*Is that the right terminology to use? I'm always hesitant to say one interface "inherits from" another, as that doesn't seem to properly capture the relationship between them. But maybe it's right on.
You're missing one huge thing -
If you implement your own interface instead of IEnumerable<T>, your class will not work with the framework methods expecting IEnumerable<T> - mainly, you will be completely unable to use LINQ, or use your class to construct a List<T>, or many other useful abstractions.
You can accomplish this, as you mention, via a separate extension method - however, this comes at a cost. By using an extension method to convert to an IEnumerable<T>, you're adding another level of abstraction required in order to use your class (which you'll do FAR more often than authoring the class), and you decrease performance (your extension method will, in effect, generate a new class implementation internally, which is really unnecessary). Most importantly, any other user of your class (or you later) will have to learn a new API that accomplishes nothing - you're making your class more difficult to use by not using standard interfaces, since it violates the user's expectations.
You're right: it does seem an overly complex solution to a pretty easy problem.
It also introduces an extra level of indirection for every step of the iteration. Probably not a performance problem, but still somewhat unnecessary when I don't think you're really gaining anything very significant.
Also, although your extension method lets you convert any IForEachable<T> into an IEnumerable<T>, it means your type itself won't satisfy a generic constraint like this:
public void Foo<T>(T collection) where T : IEnumerable
or the like. Basically, by having to perform your conversion, you're losing the ability to treat a single object as both an implementation of IEnumerable<T> and the real concrete type.
Also, by not implementing IEnumerable, you're counting yourself out of collection initializers. (I sometimes implement IEnumerable explicitly just to opt into collection initialization, throwing an exception from GetEnumerator().)
Oh, and you've also introduced an extra bit of infrastructure which is unfamiliar to everyone else in the world, compared with the vast hordes of C# developers who already know about IEnumerable<T>.
Aren't you just moving the boilerplate somewhere else - from writing the IEnumerable.GetEnumeratormethod on each class to calling your AsEnumerable extension every time an IEnumerable<T> is expected? Typically, I would expect an enumerable type to be used for querying far more times than it is written (which is exactly once). This would mean that this pattern will lead to more boilerplate, on average.
In our codebase there's probably more than 100 instances of this exact snippet:
IEnumerator IEnumerable.GetEnumerator()
{
return this.GetEnumerator();
}
And I am really OK with that. It's a tiny price to pay for full compatibility with every other piece of .NET code ever written ;)
Your proposed solution essentially requires every consumer/caller of your new interface to also remember to call a special extension method on it before it is useful.
It is so much easier to use IEnumerable for reflection. Trying to invoke generic interfaces via reflection is such a pain. The penalty of boxing via IEnumerable is lost by the overhead of reflection itself so why bother using a generic interface? As for an example, serialization comes to mind.
I think everyone has already mentioned the technical reasons not to do this, so I'll add this into the mix: requiring your user to call AsEnumerable() on your collection to be able to use enumerable extensions would violate the principle of least surprise.