12

有这个代码...

var b = new ReadOnlyCollection<int>(new[] { 2, 4, 2, 2 });
b[2] = 3;

我在第二行收到编译错误。我预计会出现运行时错误,因为ReadOnlyCollection<T>实现IList<T>并且接口中this[T]有一个设置器IList<T>

我试图复制 ReadOnlyCollection 的功能,但从中删除 setterthis[T]是一个编译错误。

4

3 回答 3

17

索引器是通过显式接口实现实现的,因此您只有在执行以下操作时才能访问它:

IList<int> b = new ReadOnlyCollection<int>(new[] { 2, 4, 2, 2 });
b[2] = 3;

或者

var b = new ReadOnlyCollection<int>(new[] { 2, 4, 2, 2 });
((IList<int>)b)[2] = 3;

当然,它会在执行时失败......

这完全是经过深思熟虑和有用的——这意味着当编译器知道它是 aReadOnlyCollection时,您将无法使用不受支持的部分功能,从而帮助您避免执行时间故障。

这是一个有趣且相对不寻常的步骤,隐式有效地实现了属性/索引器的一半,而显式地实现了一半。

与我之前的想法相反,我相信ReadOnlyCollection<T> 实际上明确地实现了整个索引器,但提供了一个公共的只读索引器。换句话说,它是这样的:

T IList<T>.this[int index]
{
    // Delegate interface implementation to "normal" implementation
    get { return this[index]; }
    set { throw new NotSupportedException("Collection is read-only."); }
}

public T this[int index]
{
    get { return ...; }
}
于 2009-09-11T09:15:58.060 回答
2

它显式地实现了 IList.Items,这使它成为非公开的,并且您必须强制转换为接口才能实现它,并实现一个新的 this[...] 索引器,它被用来代替,它只有一个获取访问器。

如果您将集合转换为 IList,您的代码将编译,但会在运行时失败。

不幸的是,我不知道如何在 C# 中执行此操作,因为在 C# 中编写索引器涉及使用this关键字,并且您不能这样编写:

T IList<T>.this[int index] { get; set; }
于 2009-09-11T09:15:48.213 回答
1

没有魔法,ReadOnlyCollection只是它自己的索引器和实现IList<T>接口的索引器有不同的实现:

public T Item[int index] { get; }

T IList<T>.Item[int index] { get; set; }

如果您将列表转换为IList<int>,您将收到运行时错误而不是编译错误:

((IList<int>)b)[2] = 3;

编辑:
要在您自己的类中实现索引器,请使用this关键字:

public T this[int index] { get { ... } }

T IList<T>.this[int index] { get { ... } set { ... } }
于 2009-09-11T09:17:53.077 回答