Collections should implement generic interface

TypeName

CollectionsShouldImplementGenericInterface

CheckId

CA1010

Category

Microsoft.Design

Breaking Change

NonBreaking

Cause

An externally visible type implements the System.Collections.IEnumerable interface but does not implement the System.Collections.Generic.IEnumerable interface, and the containing assembly targets .NET Framework 2.0. This rule ignores types that implement System.Collections.IDictionary.

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 Exclude Warnings

It is safe to exclude a warning from this rule; however, the collection will have a more limited use.

Example

The following example shows a collection, IntegerCollection, that violates the rule. In GenericIntegerCollection, the collection is modified to satisfy the rule by implementing System.Collections.Generic.IEnumerable. Finally, the collection is used to populate a strongly typed stack.

using System;
using System.Collections.ObjectModel; 

namespace Samples
{    
    public class Book        
    {                
        public Book()                
        {                
        }        
    }    
    
    public class BookCollection : Collection<Book>    
    {        
        public BookCollection()        
        {        
        }    
    }
}
using System;
using System.Collections;
using System.Collections.Generic;

namespace Samples
{
    public class Book
    {
        public Book()
        {
        }
    }

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

        public Book this [int index]
        {
            get { return this[index]; }
            set { }
        }

        int IList<Book>.IndexOf(Book item)
        {
            return 0;
        }

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

        void IList<Book>.RemoveAt(int index)
        {
        }

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

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

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

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

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

        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()
            {
            }
        }
    }
}
using System;
using System.Collections;
using System.Collections.Generic;

namespace DesignLibrary
{
   // This class violates the rule.
   public class IntegerCollection: CollectionBase
   {
      // Methods overrides using Int32.
   }

   // This class satisfies the rule.
   public class GenericIntegerCollection: 
      CollectionBase, IEnumerable<int>
   {
      public int Add(int value)
      {
         return InnerList.Add(value);
      }

      // Other method overrides using Int32.

      public new IEnumerator<int> GetEnumerator()
      {
         foreach (int data in InnerList)
         {
            yield return data;
         }
      }
   }

   class Test
   {
      static void Main()
      {
         IntegerCollection intCollection = new IntegerCollection();

         // The following line would generate a compiler error.
         //Stack<int> integerStack = new Stack<int>(intCollection);

         GenericIntegerCollection genericIntCollection =
            new GenericIntegerCollection();
         genericIntCollection.Add(2);
         genericIntCollection.Add(4);

         Stack<int> integerStack = new Stack<int>(genericIntCollection);
         Console.WriteLine(integerStack.Pop());
         Console.WriteLine(integerStack.Pop());
      }
   }
}
using System;
using System.Collections; 

namespace Samples
{    
    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);        
        }    
    }
}

The following example shows a class (reference type) that derives from the non-generic CollectionBase that violates this rule.

To fix a violation of this rule, either implement the generic interfaces or change the base class to a type that already implements both the generic and non-generic interfaces, such as Collection<T>.

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

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

The following examples fixes the above example by implement the generic interfaces, IEnumerable<T>, ICollection<T> and IList<T> (IEnumerable(Of T), ICollection(Of T) and IList(Of T) in Visual Basic)..

Avoid excessive parameters on generic types

Do not declare static members on generic types

Do not expose generic lists

Do not nest generic types in member signatures

Generic methods should provide type parameter

Use generic event handler instances

Use generics where appropriate

See Also

Concepts

Generics (C# Programming Guide)