In a response to this question runefs suggested that "unless you have a very specific reason for using IList you should considere IEnumerable". Which do you use and why?
IEnumberable<T> is read-only, you have to reconstruct the collection to make changes to it. On the other hand, IList<T> is read-write. So if you expect a lot of changes to the collection, expose IList<T> but if it's safe to assume that you won't modify it, go with IEnumerable<T>.
Always use the most restrictive interface that provides the features you need, because that gives you the most flexibility to change the implementation later. So, if IEnumerable<T> is enough, then use that... if you need list-features, use IList<T>.
And preferably use the strongly typed generic versions.
The principle I follow is one I read a while back:
"Consume the simplest and expose the
most complex"
(I'm sure this is really common and I'm mis-quoting it, if anyone can knows the source can you leave a comment or edit please...)
(Edited to add - well, here I am a couple of weeks later and I've just ran into this quote in a completely different context, it looks like it started as the Robustness Principle or Postel's Law -
Be conservative in what you do; be liberal in what you accept from others.
The original definition is for network communication over the internet, but I'm sure I've seen it repurposed for defining class contracts in OO.)
Basically, if you're defining a method for external consumption then the parameters should be the most basic type that gives you the functionality you require - in the case of your example this may mean taking in an IEnumerable instead of an IList. This gives client code the most flexibility over what they can pass in. On the other hand if you are exposing a property for external consumption then do so with the most complex type (IList or ICollection instead of IEnumerable) since this gives the client the most flexibility in the way they use the object.
I'm finding the conversation between myself and DrJokepu in the comments fascinating, but I also appreciate that this isn't supposed to be a discussion forum so I'll edit my answer to further outline the reasons behind my choice to buck the trend and suggest that you expose it as an IList (well a List actually, as you'll see). Let's say this is the class we are talking about:
public class Example
{
private List<int> _internal = new List<int>();
public /*To be decided*/ External
{
get { return _internal; }
}
}
So first of all let's assume that we are exposing External as an IEnumerable. The reasons I have seen for doing this in the other answers and comments are currently:
IEnumerable is read-only
IEnumerable is the defacto standard
It reduces coupling so you can change the implementation
You don't expose implementation details
While IEnumerable only exposes readonly functionality that doesn't make it readonly. The real type you are returning is easily found out by reflection or simply pausing a debugger and looking, so it is trivial to cast it back to List<>, the same applies to the FileStream example in the comments below. If you are trying to protect the member then pretending it is something else isn't the way to do it.
I don't believe it is the defacto standard. I can't find any code in the .NET 3.5 library where Microsoft had the option to return an concrete collection and returned an interface instead. IEnumerable is more common in 3.5 thanks to LINQ, but that is because they can't expose a more derived type, not because they don't want to.
Now, reducing coupling I agree with - to a point - basically this argument seems to be that you are telling the client that while the object you return is a list I want you to treat is as an IEnumerable just in case you decide to change the internal code later. Ignoring the problems with this and the fact that the real type is exposed anyway it still leaves the question of "why stop at IEnumerable?" if you return it as an object type then you'll have complete freedom to change the implementation to anything! This means that you must have made a decision about how much functionality the client code requires so either you are writing the client code, or the decision is based around arbitrary metrics.
Finally, as previously discussed, you can assume that implementation details are always exposed in .NET, especially if you are publicly exposing the object.
So, after that long diatribe there is one question left - Why expose it as a List<>? Well, why not? You haven't gained anything by exposing it as a IEnumerable, so why artificially limit the client code's ability to work with the object? Imagine if Microsoft had decided that the Controls collection of a Winforms Control should appear as IEnumerable instead of ControlCollection - you'd no longer be able to pass it to methods that require an IList, work on items at an arbitrary index, or see if it contains a particular control unless you cast it. Microsoft wouldn't really have gained anything, and it would just inconvenience you.
When I only need to enumerate the children, I use IEnumerable. If I happen to need Count, I use ICollection. I try to avoid that, though, because it exposes implementation details.
IList<T> is indeed a very beefy interface. I prefer to expose Collection<T>-derived types. This is pretty much in line with what Jeffrey Richter suggests doing (don't have a book nearby, so can't specify page/chapter number): methods should accept the most common types as parameters and return the most "derived" types as return values.
If a read-only collection is exposed via a property, then the conventional way to do it is to expose it as ReadOnlyCollection (or a derived class of that) wrapping whatever you have there. It exposes full capabilities of IList to the client, and yet it makes it very clear that it is read-only.
Related
I am using FxCop and it shows warning for "Don't expose generic list" which suggests use Collection<T> instead of List<T>. The reason why it is preferred, I know all that stuff, as mentioned in this SO post and MSDN and many more articles I have been through.
But my question is this, I am having few methods which does so much heavy calculation and methods accepts parameters of List<T> which is supposed to be faster and good in terms of performance. But FxCop issues warning for this as well as. So one option is that I should declare the parameter as Collection<T>, then use ToList() inside the method and then use it.
So which one is optimized?
"Suppress the warning for this case" OR "use Collection<T> in parameter and then use ToList() inside the method itself".
The code analysis/FxCop rules have been written to support framework creators (Microsoft creates a lot of frameworks). A framework is consumed by external parties and you should be careful when you design the public interface. Provided that you are not writing a framework to be consumed by external parties you can simply ignore rules that doesn't provide value to you.
However, one of the reasons that this rule exists is that exposing collections on a class is somewhat difficult. Often the elements in the collection are owned by the containing class and in that case you violate encapsulation if you allow clients to modify the collection used to store the aggregated items. By returning List<T> you allow the clients to modify the collection in many different ways. But often you want to keep track of the items in the collection. E.g. adding a new element might require some additional bookkeeping in the containing class etc. You lose this kind of control when you return a List<T> unless of course you make a copy when you return it (but then the client should understand that they only get a copy of collection and modifications will be ignored).
All in all you can probably improve your class design by avoiding exposing classes like List<T> and being more explicit about how aggregated elements can be added, modified and removed. But if you are in a hurry and just want to crank out some code then using List<T> may be exactly what you need to get the job done.
Don't bother using generic lists in public properties as long as you are not coding a framework somebody else want's to extend in the near future.
I suggest to suppress the warning. You can refactor your classes later if requirements change.
IMHO your interpretation of "Don't expose generic list' which suggests use collection instead of list". Is invalid.
The critical difference between collection and list is that the elements in list are ordered. Some methods may require that passed elements have order. Then we must use in parameter a list.
The key to understand delivered warning is that you should use instead of concrete class List<T> a interface IList<T>.
As the method operate on the list it is not so important what kind of list it is. The key factor is that it is a list.
Concluding the method parameters should be abstract as possible.
You should use the type that is most appropriate for your purposes (and suppress the warning if appropriate). If you're passing a bunch of items, and order and uniqueness don't matter, use a collection. If you're passing an ordered collection of items, use a list. If you're passing data such that every item is unique but order doesn't matter, use a set. Use the type that has the semantic meaning appropriate for the exchange. In a few cases where the semantics and the methods that you need don't necessarily align (suppose you need AddRange), make an exception, or use the conversion methods.
Q1 Why do new classes from .NET implement interfaces only partially?
Q2 Shall I do the same in my code?
I asked this question here, so I thought, okay that was long time ago, you can have different usage etc etc, and now such implementation is supported only for consistency reasons. But new classes also do that.
Old
int[] list = new int[] {};
IList iList = (IList)list;
ilist.Add(1); //exception here
New
ICollection c = new ConcurrentQueue<int>();
var root = c.SyncRoot; //exception here
UPDATE
I am not worried why I get exceptions, it is clear. But I don't understand why classes implement the well defined contract, but not all the members (which can lead to unpleasant run time exceptions)?
You could argue that the interfaces weren't granular enough in the original design. For example, most people never use SyncRoot - it could perhaps have been on a different interface. Likewise, it is unfortunate that no interface offers read-only indexer access, for example.
As it stands, the interfaces are what they are. It is still very convenient to implement the main IList[<T>]/ICollection[<T>]/IEnumerable[<T>] interfaces though - it offers the majority of callers access to what they need... so indexers in the first example, and Add in the second.
To be fair, they do also offer IsFixedSize and IsReadOnly - querying the first would have led you to not call Add. Re SyncRoot - that presumably can't make sense inside ConcurrentQueue<T>, and any implementation would break the logic of the type. Normally I would say "then it isn't that type; don't implement the interface", but to repeat my earlier statement... most people never use SyncRoot - so I'm OK with it ;p
One minor point, all interfaces have to be fully implemented. All methods and properties of an interface must be implemented by any implementor – otherwise the compiler. You are referring to the runtime errors that can be thrown when you call some methods of an interface.
The documentation for IList states:
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.
When you call a method that cannot be satisfied by a particular implementation then you get an exception.
Why was the interface designed this way? One can only speculate, but this particular design allows for the IsFixedSize, IsReadOnly etc. properties to change during the lifetime of an instance of the interface.
Should you design your interfaces this way? That depends on whether such a design is desirable and meets your needs.
From OOP standpoint this is simply wrong. They should either have made smaller interfaces or not put them on stuff like Arrays. However the .NET Framework designers are not stupid and they probably made some tradeoff. For example IEnumerable is required to implement IDisposable which does not make sense from OOP design standpoint but there are some performance benefits for database readers for example. So probably implementations that are hacked into a class (like your example) have some benefit but from OOP point of view they are wrong and you should not do it unless you are aware what you are trading the good design for.
I suspect the existence of partially-implemented interfaces is a consequence of a design decision not to allow a class or interface to have independent readable property and writable properties of the same name and automatically use them appropriately (e.g. if a class has a readable property 'foo' and a writable property 'foo', use the readable property for reads and the writable one for writes). This design decision made it awkward to split off the reading and writing aspects of certain interfaces.
Ideally, instead of having a single interface IList, there would have been generic contravariant interface IReadableByIndex, generic covariant interfaces IWriteableByIndex, IAppendable, IGrowableByIndex (includes various insert and delete functions), and non-generic IMovableByIndex (index-based copy, swap, and roll functions) and maybe IComparableByIndex (given two indices, compare the items). An interface IList could implement all of those, but there would be many useful subsets as well (many of which could be contravariant or covariant). Note that the existence of some non-generic routines would allow things like sorts to be implemented on any collection that implements IComparableByIndex and IMovableByIndex without having to worry about the exact type of the collection.
Unfortunately, for a split of IList to have been really useful, it would have been necessary to have IReadableByIndex and IWritableByIndex as separate interfaces. This in turn would have posed difficulties when trying to write code that would inherit both interfaces, as the compiler would complain about ambiguity when trying to use the indexed accessor. Since the IReadableByIndex and IWritableByIndex ended up having to be combined, Microsoft probably figured it may as well lump everything into IList.
I have always been taught that programming against an interface is better, so parameters on my methods I would set to IList<T> rather than List<T>..
But this means I have to cast to List<T> just to use some methods, one comes to mind is Find for example.
Why is this? Should I continue to program against interfaces, but continue to cast or revert?
I am a little bit confused why Find (for example) isn't available on the IList<T> which List<T> inherits from.
Personally I would use IList<T> rather than List<T>, but then use LINQ (Select, Where etc) instead of the List-specific methods.
Casting to List<T> removes much of the point of using IList<T> in the first place - and actually makes it more dangerous, as the implementation may be something other than List<T> at execution time.
In the case of lists you could continue programming against interfaces and use LINQ to filter your objects. You could even work with IEnumerable<T> which is even higher in the object hierarchy.
But more generally if the consumer of your API needs to call a specific method you probably haven't chosen the proper interface to expose.
I am a little bit confused why Find
(for example) isn't available on the
IList which List inherits from.
While I'm not privy to the decision process of the designers, there are a few things they were probably thinking.
1) Not putting these methods on IList keeps the intent of the contract clearer. According to MSDN, IList "Represents a collection of objects that can be individually accessed by index." Adding Find would change the contract to a searchable, indexable collection.
2) Every method you put on an interface makes it harder to implement the interface. If all of those methods were on IList, it would be much more tedious to implement IList. Especially since:
3) Most implementations of these methods would be the same. Find and several of the others on List would really be better placed on a helper class. Take for example, ReadOnlyCollection, Collection, ObservableCollection, and ReadOnlyObservableCollection. If I had to implement Find on all of those (pre-LINQ), I would make a helper class that takes IEnumerable and a predicate and just loop over the collections and have the implementations call the helper method.
4) LINQ (Not so much a reason why it didn't happen, more of why it isn't needed in the future.) With LINQ and extension methods, all IEnumerable's now "have" Find as an extension method (only they called it Where).
I think it's because IList can be different collection types (ie. an IEnumerable of some sort, an array or so).
You can use the Where extension method from System.Linq. Avoid casting back to List from IList.
If you find that the IList<T> parameter being passed between various classes is consistently being recast into List<T>, this indicates that there is a fundamental problem with your design.
From what you're describing, it's clear that you want to use polymorphism, but recasting on a consistent basis to List<T> would mean that IList<T> does not have the level of polymorphism you need.
On the other side of the coin, you simply might be targeting the wrong polymorphic method (e.g., Find rather than FirstOrDefault).
In either case, you should review your design and see what exactly you want to accomplish, and make the choice of List<T> or IList<T> based on the actual requirements, rather than conformity to style.
If you expose your method with a IList<> parameter, someone can pass, for exemple, a ReadOnlyCollection<>, witch is an IList<> but is not a List<>. So your API will crash at runtime.
If you expose a public method with a IList<> parameter, you cannot assume that it is a specific implementation of an IList<>. You must use it as an IList<> an nothing more.
If the list is some part of an Api or service that is exposed then it is probably better to have as an IList to allow the change of the implementation internally.
There is already much discussion on this topic.
No, in this case it has no sense to program to interfaces, because your List is NOT an IList, having extra methods on it.
My code is littered with collections - not an unusual thing, I suppose. However, usage of the various collection types isn't obvious nor trivial. Generally, I'd like to use the type that's exposes the "best" API, and has the least syntactic noise. (See Best practice when returning an array of values, Using list arrays - Best practices for comparable questions). There are guidelines suggesting what types to use in an API, but these are impractical in normal (non-API) code.
For instance:
new ReadOnlyCollection<Tuple<string,int>>(
new List<Tuple<string,int>> {
Tuple.Create("abc",3),
Tuple.Create("def",37)
}
)
List's are a very common datastructure, but creating them in this fashion involves quite a bit of syntactic noise - and it can easily get even worse (e.g. dictionaries). As it turns out, many lists are never changed, or at least never extended. Of course ReadOnlyCollection introduces yet more syntactic noise, and it doesn't even convey quite what I mean; after all ReadOnlyCollection may wrap a mutating collection. Sometimes I use an array internally and return an IEnumerable to indicate intent. But most of these approaches have a very low signal-to-noise ratio; and that's absolutely critical to understanding code.
For the 99% of all code that is not a public API, it's not necessary to follow Framework Guidelines: however, I still want a comprehensible code and a type that communicates intent.
So, what's the best-practice way to deal with the bog-standard task of making small collections to pass around values? Should array be preferred over List where possible? Something else entirely? What's the best way - clean, readable, reasonably efficient - of passing around such small collections? In particular, code should be obvious to future maintainers that have not read this question and don't want to read swathes of API docs yet still understand what the intent is. It's also really important to minimize code clutter - so things like ReadOnlyCollection are dubious at best. Nothing wrong with wordy types for major API's with small surfaces, but not as a general practice inside a large codebase.
What's the best way to pass around lists of values without lots of code clutter (such as explicit type parameters) but that still communicates intent clearly?
Edit: clarified that this is about making short, clear code, not about public API's.
After hopefully understanding your question, i think you have to distinguish between what you create and manage within your class and what you make available to the outside world.
Within your class you can use whatever best fits your current task (pro/cons of List vs. Array vs. Dictionary vs. LinkedList vs. etc.). But this has maybe nothing to do about what you provide in your public properties or functions.
Within your public contract (properties and functions) you should give back the least type (or even better interface) that is needed. So just an IList, ICollection, IDictionary, IEnumerable of some public type. Thous leads that your consumer classes are just awaiting interfaces instead of concrete classes and so you can change the concrete implementation at a later stage without breaking your public contract (due to performance reasons use an List<> instead of a LinkedList<> or vice versa).
Update:
So, this isn't strictly speaking new; but this question convinced me to go ahead and announce an open source project I've had in the works for a while (still a work in progress, but there's some useful stuff in there), which includes an IArray<T> interface (and implementations, naturally) that I think captures exactly what you want here: an indexed, read-only, even covariant (bonus!) interface.
Some benefits:
It's not a concrete type like ReadOnlyCollection<T>, so it doesn't tie you down to a specific implementation.
It's not just a wrapper (like ReadOnlyCollection<T>), so it "really is" read-only.
It clears the way for some really nice extension methods. So far the Tao.NET library only has two (I know, weak), but more are on the way. And you can easily make your own, too—just derive from ArrayBase<T> (also in the library) and override the this[int] and Count properties and you're done.
If this sounds promising to you, feel free to check it out and let me know what you think.
It's not 100% clear to me where you're worried about this "syntactic noise": in your code or in calling code?
If you're tolerant of some "noise" in your own encapsulated code then I would suggest wrapping a T[] array and exposing an IList<T> which happens to be a ReadOnlyCollection<T>:
class ThingsCollection
{
ReadOnlyCollection<Thing> _things;
public ThingsCollection()
{
Thing[] things = CreateThings();
_things = Array.AsReadOnly(things);
}
public IList<Thing> Things
{
get { return _things; }
}
protected virtual Thing[] CreateThings()
{
// Whatever you want, obviously.
return new Thing[0];
}
}
Yes there is some noise on your end, but it's not bad. And the interface you expose is quite clean.
Another option is to make your own interface, something like IArray<T>, which wraps a T[] and provides a get-only indexer. Then expose that. This is basically as clean as exposing a T[] but without falsely conveying the idea that items can be set by index.
I do not pass around Listss if I can possibly help it. Generally I have something else that is managing the collection in question, which exposes the collection, for example:
public class SomeCollection
{
private List<SomeObject> m_Objects = new List<SomeObject>();
// ctor
public SomeCollection()
{
// Initialise list here, or wot-not/
} // eo ctor
public List<SomeObject> Objects { get { return m_Objects; } }
} // eo class SomeCollection
And so this would be the object passed around:
public void SomeFunction(SomeCollection _collection)
{
// work with _collection.Objects
} // eo SomeFunction
I like this approach, because:
1) I can populate my values in the ctor. They're there the momeny anyone news SomeCollection.
2) I can restrict access, if I want, to the underlying list. In my example I exposed it all, but you don't have to do this. You can make it read-only if you want, or validate additions to the list, prior to adding them.
3) It's clean. Far easier to read SomeCollection than List<SomeObject> everywhere.
4) If you suddenly realise that your collection of choice is inefficient, you can change the underlying collection type without having to go and change all the places where it got passed as a parameter (can you imagine the trouble you might have with, say, List<String>?)
I agree. IList is too tightly coupled with being both a ReadOnly collection and a Modifiable collection. IList should have inherited from an IReadOnlyList.
Casting back to IReadOnlyList wouldn't require a explicit cast. Casting forward would.
1.
Define your own class which implements IEnumerator, takes an IList in the new constructor, has a read only default item property taking an index, and does not include any properties/methods that could otherwise allow your list to me manipulated.
If you later want to allow modifying the ReadOnly wrapper like IReadOnlyCollection does, you can make another class which is a wrapper around your custom ReadOnly Collection and has the Insert/Add/Remove/RemoveAt/Clear/...implemented and cache those changes.
2.
Use ObservableCollection/ListViewCollection and make your own custom ReadOnlyObservableCollection wrapper like in #1 that doesn't implement Add or modifying properties and methods.
ObservableCollection can bind to ListViewCollection in such a way that changes to ListViewCollection do not get pushed back into ObservableCollection. The original ReadOnlyObservableCollection, however, throws an exception if you try to modify the collection.
If you need backwards/forwards compatibility, make two new classes inheriting from these. Then Implement IBindingList and handle/translate CollectionChanged Event (INotifyCollectionChanged event) to the appropriate IBindingList events.
Then you can bind it to older DataGridView and WinForm controls, as well as WPF/Silverlight controls.
Microsoft has created a Guidelines for Collections document which is a very informative list of DOs and DON'Ts that address most of your question.
It's a long list so here are the most relevant ones:
DO prefer collections over arrays.
DO NOT use ArrayList or List in public APIs. (public properties, public parameters and return types of public methods)
DO NOT use Hashtable or Dictionary in public APIs.
DO NOT use weakly typed collections in public APIs.
DO use the least-specialized type possible as a parameter type. Most members taking collections as parameters use the IEnumerable interface.
AVOID using ICollection or ICollection as a parameter just to access the Count property.
DO use ReadOnlyCollection, a subclass of ReadOnlyCollection, or in rare cases IEnumerable for properties or return values representing read-only collections.
As the last point states, you shouldn't avoid ReadOnlyCollection like you were suggesting. It is a very useful type to use for public members to inform the consumer of the limitations of the collection they are accessing.
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 9 years ago.
Improve this question
Look at the specification of the ReadOnlyCollection class, it does implements the IList interface, right.
The IList interface have Add/Update/Read methods, which we call it pre-conditions of the interface. Anywhere in my application if I have an IList I should be able to do all this kind of operations.
But what about if I return a ReadOnlyCollection somewhere in my code and try to call the .Add(...) method? It throws a NotSupportedException. Do you think this is a good example of a bad design? Additionally, is this class breaking the Liskov Substitution Principle?
Why did Microsoft implemented this way? Should it be easier (and better) to make this ReadOnlyCollection implements only the IEnumerable interface (which is, by the way, already readonly)?
Yes, it is bad design indeed. The collection interfaces are lacking in .NET: there are no read-only interfaces.
Did you know that string[] implements IList<string> (and ditto for other types)? This has the same problem: you would expect that you can call Add and Remove on the interface, but it would throw.
Unfortunately, this cannot be changed anymore without breaking backwards compatibility, but I agree with you that it is very bad design. A better design would have seen separate interfaces for the read-only capabilities.
Although IList<T> interface defines Add(T) and Insert(int,T) methods, it also defines IsReadOnly property and if you read carefully definition of IList.Insert(int,T) and IList.Add(T) methods on MSDN, you can see that they both specify that methods could throw NotSupportedException if list is read-only.
Saying that it's bad design for that reason is like saying that it is also bad design because Insert(int, T) can throw ArgumentOutOfRangeException when index is negative or bigger than the size of collection.
It's not a great design, but a necessary evil in my opinion.
It's unfortunate that Microsoft didn't include something like IReadableList<> and IWriteableList<> in the framework and have IList<> itself implement both of those (or even skip IList<> altogether and have IWriteableList<> implement IReadableList<>). Problem solved.
But it's too late to change now, and if you have a situation where you need your collection to have list semantics and you'd prefer to throw an exception at runtime rather than allow mutations, then ReadOnlyCollection<> is, unfortunately, your best option.
IList has some read method and properties like Item, and IndexOf(..). If ReadOnlyCollection would implement IEnumerable only then you would miss out on those.
Whats the alternative? Having a readonly version of IList and a write version? That would complicate the entire BCL (not to talk about LINQ).
Also I don't think it violates the Liskov Substitution Principle because it is defined at the base level (of IList) that it can throw a not supported exception.
I think it's a good example of a trade off between abstraction and specialization.
You want the flexibility of IList, but you also want to impose some constraints, so what do you do? The way it's designed is a little awkward, and probably technically violates some design principles, but I'm not sure what would be better and still give you the same functionality and simplicity.
In this case it may have been better to have a separate IListReadOnly interface. However, it is easy to go down the path to crazy one time use interface proliferation land and make things very confusing.
I would say it's a bad design. Even if one accepts the concept of a moderately large interface with capability queries, there should have been other interfaces which inherited from them which would guarantee that certain behaviors be allowed. For example:
IEnumerable (much like existing one, but without reset, and no promise of what will happen if the collection is changed during enumeration)
IMultipassEnumerable (adds reset, and guarantees repeated enumerations of a changing collection will either return the same data or throw and exception)
ICountableEnumerable (a multipass enumerable, plus a 'count' property, and a method to get an enumerator and count simultaneously)
IModifiableEnumerable (an IEnumerator which will not throw if a collection is modified during enumeration by the thread doing the enumerating. The precise behavior would not be specified, but items which are unchanged during enumeration must be returned exactly once; those which are modified during enumeration must be returned at most once for each addition or modification, plus one if they existed at enumeration start. This interface itself does not provide any mutations, but would be used in conjunction with others that do).
ICopyableAsEnumerable (includes a count property, and a method to return an IEnumerable which represents a snapshot of the list; not actually an IEnumerable itself, but a useful feature for an IEnumerable to provide).
IImmutable (no members, but inheritable to create guaranteed-immutable interfaces)
IImmutableEnumerable
IImmutableCountableEnumerable
IList (could be readable, read-write, or immutable)
IImmutableList (no new members, but inherits IImmutable)
IWritableList (no new members, but is guaranteed writable)
That's just a small sampling, but should convey the design idea.
Its bad design IMO. Probably forced by backward compatibility issues, missing co-variance and contra-variance etc. etc. Now luckily they addressed it in .NET 4.5 with the:
IReadOnlyList<out T>
IReadOnlyCollection<out T>
IReadOnlyDictionary<TKey, TValue>
However I am missing "read-only" interface with "bool Contains(T)".
I think if there is a bad design going on it is a habit of adding to an IList without checking the ReadOnly property. The habit of programmers to ignore portions of an interface doesn't mean the interface is poor.
The truth is that few of us programmers ever bother to read the specifications. And truthfully there are many things that seem more exciting to me than sitting down and reading through the entire specification document. (Things like seeing if one really can hold the eyes open with toothpicks for example.) Besides, I have the limitation that I wouldn't remember everything anyway.
Having said that, one should not use an interface without at least looking at the list of properties and methods. And just what purpose do you think a boolean property named "ReadOnly" is for? Perhaps because the list can be read only for one reason or another. And if you are taking a list passed from someplace outside your own code you should check that the list is not read only before you try to add to it.