System.Collections.ObjectModel.ReadOnlyCollection<T>
如何实施System.Collections.Generic.IList<T>
但未实施其Add
方法?
我没有问为什么它没有Add
方法 - 这很明显,因为它应该是只读;我没有实现IList<T>
接口合同所要求的方法,而是要求如何离开。
答案 0 :(得分:4)
它显式地实现了该方法,以及通常会改变底层集合的其他几个方法。请参阅以下页面上的显式接口实现部分:
Add
方法的备注部分说明了它们使用显式实现的原因:
此成员是显式接口成员实现。仅当
ReadOnlyCollection<T>
实例强制转换为ICollection<T>
接口时才能使用它。
因此,开发人员无法直接在Add
的实例上调用ReadOnlyCollection<T>
方法(无论如何都没有意义)。当接口方法的实现由于对实现类的附加约束(在这种情况下,对象是只读的)而总是抛出异常时,经常使用显式接口实现。
有关详细信息,请参阅页面Explicit Interface Implementation (C# Programming Guide)
奖励用例:
我使用显式接口实现的另一个地方是接口方法的实现过于复杂,而派生类型可能会在逻辑中引入错误。例如,请考虑IOleCommandTarget.QueryStatus
此处(reference source)的复杂实施。 此代码仅用于正确行为的所有样板代码。它实际上并不提供任何命令的状态。
/// <inheritdoc/>
int IOleCommandTarget.QueryStatus(ref Guid guidCmdGroup, uint cCmds, OLECMD[] prgCmds, IntPtr pCmdText)
{
using (OleCommandText oleCommandText = OleCommandText.FromQueryStatus(pCmdText))
{
Guid cmdGroup = guidCmdGroup;
for (uint i = 0; i < cCmds; i++)
{
OLECMDF status = QueryCommandStatus(ref cmdGroup, prgCmds[i].cmdID, oleCommandText);
if (status == default(OLECMDF) && _next != null)
{
int hr = _next.QueryStatus(ref cmdGroup, cCmds, prgCmds, pCmdText);
if (ErrorHandler.Failed(hr))
return hr;
}
else
{
prgCmds[i].cmdf = (uint)status;
}
}
return VSConstants.S_OK;
}
}
我将以下更简单的方法公开为protected virtual
而不是仅仅创建接口方法public virtual
,派生类型不需要担心正确解释pCmdText
参数或如何处理对_next
(reference source)中的每个项目prgCmds
。
/// <summary>
/// Gets the current status of a particular command.
/// </summary>
/// <remarks>
/// The base implementation returns 0 for all commands, indicating the command is
/// not supported by this command filter.
/// </remarks>
/// <param name="commandGroup">The command group.</param>
/// <param name="commandId">The command ID.</param>
/// <param name="oleCommandText">A wrapper around the <see cref="OLECMDTEXT"/>
/// object passed to <see cref="IOleCommandTarget.QueryStatus"/>, or
/// <see langword="null"/> if this parameter should be ignored.</param>
/// <returns>A collection of <see cref="OLECMDF"/> flags indicating the current
/// status of a particular command, or 0 if the command is not supported by the
/// current command filter.</returns>
protected virtual OLECMDF QueryCommandStatus(ref Guid commandGroup, uint commandId, OleCommandText oleCommandText)
{
return default(OLECMDF);
}
答案 1 :(得分:1)
查看ReadOnlyCollection<>
实现,可以看到Add
方法是这样实现的:
int IList.Add(object value)
{
ThrowHelper.ThrowNotSupportedException(ExceptionResource.NotSupported_ReadOnlyCollection);
return -1;
}
正如您所看到的,它实际上是IList.Add
Add
方法的explicit implementation。这解释了为什么ReadOnlyCollection<>
方法不能直接从IList
中获取。如果您将值转换为NotSupportedException
,那么将能够调用它,但这将导致IList
被抛出。
请注意,其他Clear
方法(例如Insert
,Remove
和ICollection<>
)也以类似的方式在此类中实现,以及一些方法System.Collections.Generic.IReadOnlyCollection<>
也是:他们明确实施并抛出异常。
从.NET 4.5开始,ReadOnlyCollection<>
现在存在。虽然IList
类实现了它,但它仍然继续实现IList
,我认为这是出于反向兼容的原因。尽管如此,如果您使用4.5并考虑自己实现只读集合,我会建议您不要实现IReadOnlyCollection<>
并使用{{1}}代替
答案 2 :(得分:1)
以这种方式实施:
int IList.Add(object value)
{
ThrowHelper.ThrowNotSupportedException(ExceptionResource.NotSupported_ReadOnlyCollection);
return -1;
}
它还记录在MSDN:
将项目添加到IList。此实现始终抛出NotSupportedException。