为什么string不实现IList <char>?</char>

时间:2011-08-20 23:02:07

标签: c# .net string

标题说明所有

为什么String实施IEnumerable<char>而不是IList<char>

字符串具有长度,您可以从特定索引中获取元素 并且可以表明它与ICollection<char>.IsReadOnly不可变。

那么它有什么问题呢?我错过了什么吗?

许多答案指出: 没有单独的只读列表/集合的界面,但正如ReadOnlyCollection<T>所示,我认为它绝对可能并且使用IsReadOnly为这种情况设计的属性。

   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();
        }

        // ...
    }

6 个答案:

答案 0 :(得分:14)

主要是因为IList继承自ICollection,并且字符串不支持可变beahviors ICollection承诺(添加,删除和清除) - 当您添加或删除字符元素时一个字符串,它会创建一个新字符串。


实现接口的部分是糟糕的设计。你有时可能会出于必要而做,但这绝不是一个好的选择。分割界面更好。 (可能已经拆分了.NET集合接口以将可变成员与诊断成员隔离开。)IsReadOnly之类的元函数使得界面不那么连贯并且使其更难使用。

所以即使一个字符串在很多方面都是一个列表 - 你可以索引一个字符串并找到其中的字符数 - 很少有人真正想要处理像这样的字符串一个IList<char>,特别是在3.5后世界,当有简单的转换时,可以轻松地从IEnumerable<char>获取此信息。

答案 1 :(得分:4)

ReadOnlyCollection支持IList的事实在我看来非常高: meh

IList是一个合同,表明实现者支持在不可变对象上明显错误的收集变异(例如Add,Remove,Insert)。将其转移到System.String是完全错误的,因为它也是设计不可变的。

System.String实施IList将是一个糟糕的API设计,因为一堆方法不起作用,因此字符串的工作方式与完全实现IList的类型不同。

也许你正在跳跃它支持更自由的界面,当然,IList不是正确的选择。

这样的部分接口实现会破坏Liskov substitution principle,并引入潜在的运行时错误。

<强>更新

有趣的是,.Net 4.5引入了新的IReadOnlyList接口。但是,String未实现它,并且无法将其引入IList层次结构。

一些背景:http://www.infoq.com/news/2011/10/ReadOnly-WInRT

答案 2 :(得分:2)

为什么要这样?我认为这种情况并不多见。大多数情况下,当你有字符串时,你知道它是一个字符串。如果不这样做,IEnumerable<char>通常就足够了。

IList<T>(和ICollection<T>)是可变集合的接口,string不是。{1}}。 (虽然ReadOnlyCollection<T>确实实现了它们。)

答案 3 :(得分:1)

主要是,它不是IList(你不能"hello world".Add('!') - 这是一个接口和合同不兼容;它不仅仅是一个只读列表,因为它会“知道”{{1} 1}}操作,并抛出调用)。


<子>

字符串也有特殊的语义 - 存储优化和身份别名(可以有小字符串优化,可以有实习字符串)。一旦你作为Add传递,这些就不会突出 - 人们可能会开始期待'正常'的List&lt;&gt; -like语义。

然而,当看到IList<char>时,没有提出这样的期望(它只是说:我能够连续给你一些角色,你不需要知道它们来自哪里)。

答案 4 :(得分:1)

我不确定为什么下面的替代方案尚未提出......

System.String支持此(尽管是间接的)。是的,它在内存中创建了另一个副本,但我相信这是设计的,因为字符串是不可变的。

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

答案 5 :(得分:0)

String未实现IList<char>的事实并不让我感到惊讶。事实上,令我感到惊讶的是,它实现了IEnumerable<char>

为什么呢?因为字符串实际上不是字符序列。

你看,Unicode中有1,114,112个代码点,但char只有16位。该字符串包含一系列字符(即unicode代码点),使用多个char值的UTF16进行编码。因此,字符串中的unicode字符数可能小于字符串中char个值的数量。

现在,我确实认识到这对大多数人来说听起来很奇怪,特别是那些说英语的人,因为他们一直对ASCII感到满意,并假设一个char等于一个字符。在许多情况下,这种假设甚至是正确的。这可能是string实现IEnumerable<char>的原因,因为它与传统的非unicode世界有某种妥协。

但事实是,我们无法回答你的问题。唯一可以告诉你为什么他们按照他们的方式设计字符串的人是当时BCL团队的人。