标题说明所有
为什么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();
}
// ...
}
答案 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
层次结构。
答案 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团队的人。