Why doesn't a string implement an IList <char>?

The title is all

Why does String implement an IEnumerable<char> rather than an IList<char> ?

The string has a length, and you can already take elements from a specific index.
And you can specify that it is immutable with ICollection<char>.IsReadOnly .

So what could be wrong with him? Did I miss something?

As many answers point to this: A read-only list / collection interface does not exist, but as ReadOnlyCollection<T> shows, I think it is definitely possible and with it IsReadOnly Property intended for such cases.

  public class String : IList<char> { int IList<char>.IndexOf(char item) { // ... } void IList<char>.Insert(int index, char item) { throw new NotSupportedException(); } void IList<char>.RemoveAt(int index) { throw new NotSupportedException(); } char IList<char>.this[int index] { set { throw new NotSupportedException(); } } void ICollection<char>.Add(char item) { throw new NotSupportedException(); } void ICollection<char>.Clear() { throw new NotSupportedException(); } public bool Contains(char item) { // ... } public void CopyTo(char[] array, int arrayIndex) { // ... } int ICollection<char>.Count { get { return this.Length; } } public bool IsReadOnly { get { return true; } } bool ICollection<char>.Remove(char item) { throw new NotSupportedException(); } // ... } 
+4
source share
6 answers

This is mainly because IList inherits from ICollection , and strings do not support mutable beahviors ICollection promises (add, delete, and clear) - when adding or removing character elements from a string, a new line is created.


The implementation of the interface part is poor design. You can do this sometimes out of necessity, but it will never be a good choice. Better to split the interface. (The interfaces in the .NET collection should probably be separated in order to separate mutable elements from diagnostic ones.) Meta-functions like IsReadOnly make the interface less consistent and make it harder to use.

Thus, although a string is in many respects a list , you can index a string and find the number of characters in it - it is quite rare that someone really wants to process a string such as a IList<char> , especially in the world after 3.5 when there are simple conversions that make it easy to get this information from an IEnumerable<char> .

+14
source

The fact that ReadOnlyCollection supports IList , in my opinion, very high: meh .

IList is a contract that indicates that developers support a mutation in the collection (for example, Add, Remove, Insert), which is clearly incorrect on an immutable object. Running this parameter before System.String simply incorrect, as it is also unchanged in design.

System.String implementation of IList will be a terrible API construct, since a bunch of methods will not work, so strings will not work like types that fully implement IList .

You may be jumping to support a more liberal interface, of course, but IList not the right choice.

Implementing a partial interface like this violates the Liskov substitution principle and introduces possible runtime errors.

Update

Interestingly, .Net 4.5 introduces the new IReadOnlyList interface. However, String does not implement it and cannot be entered into the IList hierarchy.

Some background: http://www.infoq.com/news/2011/10/ReadOnly-WInRT .

+4
source

Why? I think there are not many cases where this would be useful. In most cases, when you have a string, you know it as a string. And when you don't, IEnumerable<char> often good enough.

What else IList<T> (and ICollection<T> ) are interfaces for mutable collections that are not string . (Although ReadOnlyCollection<T> does them.)

+2
source

Basically, this is not an IList (you cannot "hello world".Add('!') - this is an incompatibility of the interface and the contract, it is not just a read-only list, because it will "know" what the Add operation is, and quit call).


<sub>

In addition, strings have special semantics - storage optimization and alias aliases come to mind (there may be small string optimizations, there may be interned strings). They do not stand out after you pass as IList<char> - people may start to expect a "normal" List <> - as semantics.

However, when you see an IEnumerable<char> , such expectations do not occur (it just says: I can give you several characters in a row, you do not need to know where they came from).

sub>

+1
source

I'm not sure why the alternative below has not yet been brought up ...

System.String supports this (albeit indirectly). Yes, it makes another copy in memory, but I believe that it is designed that way because the lines are immutable.

 string myString = "hello world"; IList<char> myIList = myString.ToCharArray(); 
+1
source

The fact that String does not implement IList<char> does not surprise me. In fact, what surprises me somewhat is that it implements an IEnumerable<char> .

Why? Since the string is not really a sequence of characters.

You see, there are 1114112 code points in Unicode, but char is only 16 bits. The string contains a sequence of characters (for example, Unicode codes), which is encoded using UTF16 in the number of char values. As a result, the number of Unicode characters in a string may be less than the number of char values ​​in a string.

Now I understand that this sounds very strange to most people, especially those who speak English, because they were always happy with ASCII and suggested that one char is equal to one character. In many cases, this assumption is even true. This may be the reason that String implements IEnumerable<char> , as a kind of compromise with the inherited, non-unicode world.

But in fact, we can not answer your question. The only ones who can tell you why they designed the lines the way they did were the people on the BCL team at the time.

0
source

All Articles