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

て烟熏妆下的殇ゞ 提交于 2021-01-27 04:53:08

问题


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


回答1:


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




回答2:


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



回答3:


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



回答4:


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"});



回答5:


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.



来源:https://stackoverflow.com/questions/14816880/how-to-set-a-generic-constraint-that-the-type-is-a-list

标签
易学教程内所有资源均来自网络或用户发布的内容,如有违反法律规定的内容欢迎反馈
该文章没有解决你所遇到的问题?点击提问,说说你的问题,让更多的人一起探讨吧!