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:
- System.Collections.Generic.List<T>
- System.Collections.Generic.Queue<T>
- System.Collections.Generic.Stack<T>
How to fix violations
To fix a violation of this rule, implement one of the following generic collection interfaces:
- System.Collections.Generic.IEnumerable<T>
- System.Collections.Generic.ICollection<T>
- System.Collections.Generic.IList<T>
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:
- Implement the generic interface.
- Change the base class to a type that already implements both the generic and non-generic interfaces, such as the
Collection<T>
class.
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.
Related rules
- CA1005: Avoid excessive parameters on generic types
- CA1000: Do not declare static members on generic types
- CA1002: Do not expose generic lists
- CA1003: Use generic event handler instances