Logo Questions Linux Laravel Mysql Ubuntu Git Menu
 

ReadOnlyCollection<T> implements IList<T> but doesn't implement add method

Tags:

.net

How is it that System.Collections.ObjectModel.ReadOnlyCollection<T> implements System.Collections.Generic.IList<T> but doesn't implement its Add method?

I'm not asking why it doesn't have an Add method -- that's obvious, because it's supposed to be read-only; I'm asking how it can get away without implementing the method, which is required by the IList<T> interface contract.

like image 280
rory.ap Avatar asked Mar 21 '14 14:03

rory.ap


2 Answers

It explicitly implements the method, along with several others that would generally alter the underlying collection. See the section Explicit Interface Implementations on the following page:

ReadOnlyCollection<T>

The remarks section of the Add method indicates why they used an explicit implementation:

This member is an explicit interface member implementation. It can be used only when the ReadOnlyCollection<T> instance is cast to an ICollection<T> interface.

As a result, developers are prevented from calling the Add method directly on an instance of ReadOnlyCollection<T> (which wouldn't make sense anyway). Explicit interface implementations are frequently used when the implementation of an interface method would always throw an exception due to additional constraints placed on the implementing class (in this case, the object is read-only).

For more information, see the page Explicit Interface Implementation (C# Programming Guide)


Bonus use case:

Another place where I use explicit interface implementations is when the implementation of the interface method is overly complex, and derived types might introduce an error in the logic. For example, consider the complex implementation of IOleCommandTarget.QueryStatus here (reference source). This code is all boiler-plate code only for the purpose of correct behavior. It does not actually provide the status of any commands.

/// <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;
    }
}

My exposing the following simpler method as protected virtual instead of just making the interface method public virtual, derived types don't need to worry about properly interpreting the pCmdText parameter or how to handle _next for each of the items in prgCmds (reference source).

/// <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);
}
like image 163
Sam Harwell Avatar answered Nov 15 '22 10:11

Sam Harwell


It's implemented in this way:

int IList.Add(object value)
{
    ThrowHelper.ThrowNotSupportedException(ExceptionResource.NotSupported_ReadOnlyCollection);
    return -1;
}

It's also documented at MSDN:

Adds an item to the IList. This implementation always throws NotSupportedException.

like image 21
Tim Schmelter Avatar answered Nov 15 '22 09:11

Tim Schmelter