ReadOnlyCollection <t>实现IList <t>但没有实现add方法</t> </t>

时间:2014-03-21 14:33:29

标签: .net

System.Collections.ObjectModel.ReadOnlyCollection<T>如何实施System.Collections.Generic.IList<T>但未实施其Add方法?

我没有问为什么它没有Add方法 - 这很明显,因为它应该是只读;我没有实现IList<T>接口合同所要求的方法,而是要求如何离开。

3 个答案:

答案 0 :(得分:4)

它显式地实现了该方法,以及通常会改变底层集合的其他几个方法。请参阅以下页面上的显式接口实现部分:

ReadOnlyCollection<T>

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方法(例如InsertRemoveICollection<>)也以类似的方式在此类中实现,以及一些方法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。