简体   繁体   中英

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

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 :(

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

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).

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

if you want to use parametric polymorphism to express that constraint, in c# you need two type parameters (since you don't get higher-order types):

private static void PrintEachItemInList<X, T>(X anyType) where X:System.Collections.Generic.List<T>
{

             foreach (var t in anyType)
            {
                Console.WriteLine(t.ToString());
            }
}

but then you need to call like this:

PrintEachItemInList<List<string>,string>(new List<string>() {"a", "b"});

You would typically write this as:

private static void PrintEachItemInList<T>(List<T> anyType)
{
  // do work
}

However, in this case, I would recommend using IEnumerable<T> instead:

private static void PrintEachItemInList<T>(IEnumerable<T> anyType)
{
  // do work
}

This will still allow you to use foreach but allow your method to be more flexible, as it will work work List<T> but also any other collection which implements IEnumerable<T> . If you must have list semantics within the method, you could use IList<T> , but your sample code (and method name) suggests IEnumerable<T> would be more appropriate.

The technical post webpages of this site follow the CC BY-SA 4.0 protocol. If you need to reprint, please indicate the site URL or the original address.Any question please contact:yoyou2525@163.com.

 
粤ICP备18138465号  © 2020-2024 STACKOOM.COM