CA1010: Collections should implement generic interface

Property Value
Rule ID CA1010
Title Collections should implement generic interface
Category Design
Fix is breaking or non-breaking Non-breaking
Enabled by default in .NET 8 No

Cause

A type implements the System.Collections.IEnumerable interface but does not implement the System.Collections.Generic.IEnumerable<T> interface, and the containing assembly targets .NET. This rule ignores types that implement System.Collections.IDictionary.

By default, this rule only looks at externally visible types, but this is configurable. You can also configure additional interfaces to require that a generic interface be implemented.

Rule description

To broaden the usability of a collection, implement one of the generic collection interfaces. Then the collection can be used to populate generic collection types such as the following:

How to fix violations

To fix a violation of this rule, implement one of the following generic collection interfaces:

When to suppress warnings

It is safe to suppress a warning from this rule; however, the use of the collection will be more limited.

Suppress a warning

If you just want to suppress a single violation, add preprocessor directives to your source file to disable and then re-enable the rule.

#pragma warning disable CA1010
// The code that's violating the rule is on this line.
#pragma warning restore CA1010

To disable the rule for a file, folder, or project, set its severity to none in the configuration file.

[*.{cs,vb}]
dotnet_diagnostic.CA1010.severity = none

For more information, see How to suppress code analysis warnings.

Configure code to analyze

Use the following options to configure which parts of your codebase to run this rule on.

You can configure these options for just this rule, for all rules it applies to, or for all rules in this category (Design) that it applies to. For more information, see Code quality rule configuration options.

Include specific API surfaces

You can configure which parts of your codebase to run this rule on, based on their accessibility. For example, to specify that the rule should run only against the non-public API surface, add the following key-value pair to an .editorconfig file in your project:

dotnet_code_quality.CAXXXX.api_surface = private, internal

Additional required generic interfaces

You can configure the list of interface names (separated by |) with their required generic fully qualified interface (separated by ->).

Allowed interface formats:

  • Interface name only (includes all interfaces with the name, regardless of the containing type or namespace).
  • Fully qualified names in the symbol's documentation ID format with an optional T: prefix.

Examples:

Option value Summary
dotnet_code_quality.CA1010.additional_required_generic_interfaces = ISomething->System.Collections.Generic.IEnumerable`1 All types that implement ISomething regardless of its namespace are expected to also implement System.Collections.Generic.IEnumerable<T>.
dotnet_code_quality.CA1010.additional_required_generic_interfaces = T:System.Collections.IDictionary->T:System.Collections.Generic.IDictionary`2 All types that implement System.Collections.IDictionary are expected to also implement System.Collections.Generic.IDictionary<TKey,TValue>.

Example

The following example shows a class that derives from the non-generic CollectionBase class and violates this rule.

public class Book
{
    public Book()
    {
    }
}

public class BookCollection : CollectionBase
{
    public BookCollection()
    {
    }

    public void Add(Book value)
    {
        InnerList.Add(value);
    }

    public void Remove(Book value)
    {
        InnerList.Remove(value);
    }

    public void Insert(int index, Book value)
    {
        InnerList.Insert(index, value);
    }

    public Book? this[int index]
    {
        get { return (Book?)InnerList[index]; }
        set { InnerList[index] = value; }
    }

    public bool Contains(Book value)
    {
        return InnerList.Contains(value);
    }

    public int IndexOf(Book value)
    {
        return InnerList.IndexOf(value);
    }

    public void CopyTo(Book[] array, int arrayIndex)
    {
        InnerList.CopyTo(array, arrayIndex);
    }
}

To fix a violation of this rule, do one of the following:

Fix by interface implementation

The following example fixes the violation by implementing these generic interfaces: IEnumerable<T>, ICollection<T>, and IList<T>.

public class Book
{
    public Book()
    {
    }
}

public class BookCollection : CollectionBase, IList<Book?>
{
    public BookCollection()
    {
    }

    int IList<Book?>.IndexOf(Book? item)
    {
        return this.List.IndexOf(item);
    }

    void IList<Book?>.Insert(int location, Book? item)
    {
    }

    Book? IList<Book?>.this[int index]
    {
        get => (Book?)this.List[index];
        set { }
    }

    void ICollection<Book?>.Add(Book? item)
    {
    }

    bool ICollection<Book?>.Contains(Book? item)
    {
        return true;
    }

    void ICollection<Book?>.CopyTo(Book?[] array, int arrayIndex)
    {
    }

    bool ICollection<Book?>.IsReadOnly
    {
        get { return false; }
    }

    bool ICollection<Book?>.Remove(Book? item)
    {
        if (InnerList.Contains(item))
        {
            InnerList.Remove(item);
            return true;
        }
        return false;
    }

    IEnumerator<Book> IEnumerable<Book?>.GetEnumerator()
    {
        return new BookCollectionEnumerator(InnerList.GetEnumerator());
    }

    private class BookCollectionEnumerator : IEnumerator<Book>
    {
        private IEnumerator _Enumerator;

        public BookCollectionEnumerator(IEnumerator enumerator)
        {
            _Enumerator = enumerator;
        }

        public Book Current
        {
            get { return (Book)_Enumerator.Current; }
        }

        object IEnumerator.Current
        {
            get { return _Enumerator.Current; }
        }

        public bool MoveNext()
        {
            return _Enumerator.MoveNext();
        }

        public void Reset()
        {
            _Enumerator.Reset();
        }

        public void Dispose()
        {
        }
    }
}

Fix by base class change

The following example fixes the violation by changing the base class of the collection from the non-generic CollectionBase class to the generic Collection<T> (Collection(Of T) in Visual Basic) class.

public class Book
{
    public Book()
    {
    }
}

public class BookCollection : Collection<Book>
{
    public BookCollection()
    {
    }
}

Changing the base class of an already released class is considered a breaking change to existing consumers.

See also