Logo Questions Linux Laravel Mysql Ubuntu Git Menu
 

How to set a generic constraint that the type is a List?

Tags:

c#

generics

private static void PrintEachItemInList<T>(T anyList) 
Where T:System.Collections.Generic.List<T>
{    
    foreach (var t in T)
    {
        //Do whatever
    }
}

In the above code (which is wrong) all I want to do it to set a constraint that T is a List.

The aim is not to get this example to work, the aim is to understand how can I set a constraint that the type is a list? I am an amateur in generics and am trying to figure things out :(

like image 434
developer747 Avatar asked Feb 11 '13 16:02

developer747


3 Answers

Maybe you want two type parameters, as in:

private static void PrintEachItemInList<TList, TItem>(TList anyType) 
  where TList : System.Collections.Generic.List<TItem>

This is useful if you use classes that actually derive from List<>. If you want anything that acts as a list, consider constraining to the interface IList<> instead. It will then work for List<>, single-dimensional arrays, and custom classes implementing the interface (but not necessarily deriving from List<>).

Edit: As pointed out by the comment, this method is cumbersome to use because the compiler will not infer the two type arguments, so they will have to be given explicitly when calling the method.

Consider just using:

private static void PrintEachItemInList<TItem>(List<TItem> anyType) 

Because anything which derives from a List<> is assignable to List<>, the method can be called with derived classes as arguments, and in many cases the type TItem can be inferred automatically by the compiler.

Still consider using the interface IList<>.

If all you want to do, is read from the list, use IReadOnlyList<TItem> instead of IList<TItem>. This signals to the caller that you won't change his list. Still no cast syntax is required when calling, for example: PrintEachItemInList(new[] { 2, 3, 5, 7, });. The type IReadOnlyList<> is new in .NET version 4.5.

If all you want to do is read from the list, and you don't want to use the indexer (no anyType[idx]), and you don't want to use the .Count property, and in fact, all you want to do is foreach through the list, use IEnumerable<TItem>. Again, you signal that you won't change people's lists.

Both IReadOnlyList<> and IEnumerable<> are covariant in their generic argument (type parameter).

like image 61
Jeppe Stig Nielsen Avatar answered Nov 12 '22 21:11

Jeppe Stig Nielsen


Declare your input parameter as IList<T>. If you want to make your input sequence as abstract as possible - use IEnumerable<T> instead of IList<T>

private static void PrintEachItemInList<T>(IList<T> sequence)
{
    foreach (T element in sequence)
    {
        //Do whatever
    }
} 
like image 6
Ilya Ivanov Avatar answered Nov 12 '22 20:11

Ilya Ivanov


Your function should simply accept a list, and you don't need a constraint:

private static void PrintEachItemInList<T>(IList<T> list) 
{
    foreach (var t in list)
    {
        //Do whatever
    }
}

However, if you only want to iterate the list you can make your code more general by changing the parameter type to one of these base interfaces:

  • IEnumerable<T> - allows foreach
  • IReadOnlyCollection - same as above and provides the count of elements in the collection
  • IReadOnlyList - same as above and allows element access by index
  • ICollection<T> - an IEnumerable<T> that provides an element count and methods to add and remove elements
  • IList<T> - same as above and allows you to add and remove elements by index
like image 3
Martin Liversage Avatar answered Nov 12 '22 21:11

Martin Liversage