简体   繁体   English

C# 创建一个包含泛型集合的泛型方法并调用 Sum()

[英]C# Create a Generic method contains generic collection and invoke Sum()

using System;
using System.Collections.Generic;
using System.Linq;

public class Test
{
    public static void Fun<T>(List<T> list)
    {
        // The foreach works
        foreach(T i in list)
        {
            Console.WriteLine(i);
        }

        //The following line has error: List<T>' does not contain a definition for 'Sum'
        Console.WriteLine(list.Sum());
    }

     public static void Main()
    {
        var arr = new int[]{1,2,3,4};
        Fun<int>(arr.ToList());
    }
}

the foreach works, but list.Sum() doesn't. foreach有效,但list.Sum()无效。 It shows:表明:

'List' does not contain a definition for 'Sum' and the best extension method overload 'Queryable.Sum(IQueryable)' requires a receiver of type 'IQueryable' “List”不包含“Sum”的定义,并且最佳扩展方法重载“Queryable.Sum(IQueryable)”需要“IQueryable”类型的接收器

if I declare as public static void Fun(List<int> list) , then it works, anybody know why?如果我声明为public static void Fun(List<int> list) ,那么它可以工作,有人知道为什么吗?

a similar C++ version can work:类似的 C++ 版本可以工作:

#include <iostream>
#include <vector>
using namespace std;

template<class T>
struct MyList
{
    MyList(vector<T> v) : m(v){};
    vector<T> m;
    T Sum()
    {
        T sum = T();
        for(auto i : m)
        {
            sum += i;
        }
        return sum;
    }
};



template<class T>
void Fun(MyList<T> list)
{
    cout << list.Sum() << endl;
}

int main(int argc, char *argv[])
{
    Fun(MyList<int>({1,2,3}));
    Fun(MyList<double>({1.0,2.0,3.0}));
}

output: 
6
6.3

The difference between your C++ code and C# code is that in your C++ code, your MyList<T> template explicitly defines a Sum method.您的 C++ 代码和 C# 代码之间的区别在于,在您的 C++ 代码中,您的 MyList MyList<T>模板明确定义了Sum方法。 In contrast, C#'s List<T> doesn't define a Sum() method.相反,C# 的List<T>没有定义Sum()方法。 Rather, List<T> implements IEnumerable<T> , and there are extension methods in the System.Linq namespace to provide Sum functionality to certain closed types of IEnumerable<T> .相反, List<T>实现IEnumerable<T> ,并且在System.Linq命名空间中有扩展方法为某些封闭类型的IEnumerable<T>提供Sum功能。

Referring to the documentation , you'll see that Sum (without a selector, so not something like Sum(x => x.Value) ) is only defined for the following closed types:参考文档,您会看到Sum (没有选择器,所以不是Sum(x => x.Value)类的东西)仅针对以下封闭类型定义:

  • IEnumerable<Single>
  • IEnumerable<Int32>
  • IEnumerable<Int64>
  • IEnumerable<Double>
  • IEnumerable<Decimal>
  • IEnumerable<Nullable<Single>>
  • IEnumerable<Nullable<Int32>>
  • IEnumerable<Nullable<Int64>>
  • IEnumerable<Nullable<Double>>
  • IEnumerable<Nullable<Decimal>>

Therefore, it is not a feature of List<T> , and there is now way for the compiler to verify at compile time that the T in List<T> will be one of these types.因此,它不是List<T>的一个特性,现在编译器可以在编译时验证List<T>中的T是否是这些类型之一。

Note that long is the same as Int64 , int is the same as Int32 , and single is the same as float .注意longInt64相同, intInt32相同, singlefloat相同。

You could possibly provide some kind of aggregation functionality with the aid of a delegate:您可以借助委托提供某种聚合功能:

public static void Fun<T>(List<T> list, Func<T, T, T> sumDelegate)
{
    T sumValue = default(T);
    // The foreach works
    foreach(T i in list)
    {
        Console.WriteLine(i);
        sumValue = sumDelegate(sumValue, i);
    }

    //The following line has error: List<T>' does not contain a definition for 'Sum'
    Console.WriteLine(sumValue);
}

And then call it like so:然后这样称呼它:

var arr = new int[]{1,2,3,4};
Fun<int>(arr.ToList(), (x, y) => x + y);

Try it online在线尝试

I don't know, how C++ works in this case, but in your code generic type parameter T isn't constrained to any type and compiler doesn't know whether T supports + operator or not.我不知道 C++ 在这种情况下如何工作,但是在您的代码中,泛型类型参数T不受任何类型的限制,编译器不知道T是否支持+运算符。 You can use dynamic keyword here to allow the compiler to determine the type in runtime您可以在此处使用dynamic关键字以允许编译器在运行时确定类型

public static void Fun<T>(List<T> list)
{
    dynamic sum = default(T);
    foreach (T i in list)
    {
        Console.WriteLine(i);
        sum += (dynamic)i;
    }            

    Console.WriteLine(sum);
}

Or just in one line var sum = list.Aggregate((arg1, arg2) => (dynamic)arg1 + arg2);或者只是在一行var sum = list.Aggregate((arg1, arg2) => (dynamic)arg1 + arg2);

But you should be very careful with it.但是你应该非常小心。 If you pass a type, which doesn't support or overload the + , you'll get an exception in runtime.如果您传递一个不支持或重载+的类型,您将在运行时遇到异常。 You can apply generic constraint to T and allow it match only numeric types where T: struct, IComparable, IComparable<T>, IConvertible, IEquatable<T>, IFormattable (it's quite long, but there is no generic constraint to match a numeric types in C# for now)您可以将泛型约束应用于T并允许它仅匹配where T: struct, IComparable, IComparable<T>, IConvertible, IEquatable<T>, IFormattable (它很长,但没有泛型约束来匹配数字类型在 C# 中)

声明:本站的技术帖子网页,遵循CC BY-SA 4.0协议,如果您需要转载,请注明本站网址或者原文地址。任何问题请咨询:yoyou2525@163.com.

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