如何区分T和IList <t>

时间:2017-04-11 02:11:56

标签: c# generics

我有两种方法:

public static int Insert<T>(this System.Data.IDbConnection connection, T param)
public static int Insert<T>(this System.Data.IDbConnection connection, IList<T> param)

当我尝试这样的事情时:

connection.Insert(new List<Foo>());

调用错误的方法(第一种方法)。

我怎样才能让它发挥作用?

4 个答案:

答案 0 :(得分:5)

如果存在可以以相同方式隐式调用的泛型重载,则必须使用显式调用。

此代码将调用第二个重载。

connection.Insert<Foo>(new List<Foo>());

答案 1 :(得分:2)

这个原型:

public static int Insert<T>(this System.Data.IDbConnection connection, T param)

...几乎可以接受任何param,因为它没有类型限制。它会接受FooIList<Foo>List<Foo>以及非Foo的所有内容。因此,它与第二个原型重叠,这个问题称为收敛。

如果可能的话,最好避免整个混乱。如果可以,可以更狭义地定义第一个原型,如下所示:

public static int Insert<T>(this IDbConnection connection, T param) where T: Foo

然后只有当TFooFoo的后代时才会调用它。 List<>IList<>都不属于Foo,因此应解决您的问题。

答案 2 :(得分:1)

您可以指定通用类型,而不是依赖于类型推断

connection.Insert<Foo>(new List<Foo>());

或者您可以显式转换参数以匹配方法的签名,以帮助编译器。

connection.Insert((IList<Foo>)new List<Foo>());

答案 3 :(得分:1)

我看到以下选项:

  • 如果您不想重写方法签名,则应该帮助编译器选择特定的重载。

    您可以指定通用参数

    connection.Insert<Foo>(new List<Foo>());

    或转换为IList<T>

    connection.Insert((IList<Foo>)new List<Foo>());

  • 如果您不想在每次调用方法时都考虑过,并且您可以自由添加更多重载 - 那么最简单的方法就是为所有可能的IList实现添加重载会像这样使用:

    public static int Insert<T>(this IDbConnection connection, List<T> param)
    {
        return connection.Insert((IList<T>)param);
    }
    
  • 如果您根本不想更改公共接口,但仍然不想在任何地方进行转换 - 您需要更改最通用方法的主体以评估传递的参数,并可能在内部传递给它其他重载,如:

    public static int Insert<T>(this IDbConnection connection, T param)
    {
        if (typeof(T).GetInterfaces()
          .Any(x => x.IsGenericType && x.GetGenericTypeDefinition() == typeof(IList<>)))
        {
            // method info retrieval should be written more carefully & cached in static var
            var method = MethodBase.GetCurrentMethod().DeclaringType.GetMethods()
              .Single(m => m.Name == "Insert" && m.GetParameters()
                .Select(p => p.ParameterType)
                .Any(t => t.IsGenericType && t.GetGenericTypeDefinition() == typeof(IList<>)));
            var generic = method.MakeGenericMethod(typeof(T).GenericTypeArguments[0]);
            return (int)generic.Invoke(null, new object[] { connection, param });
        }
    
        ...
    }
    
  • 好的,如果你不喜欢这一切 - 就像那些来自.NET团队的人一样,将重载重命名为InsertRange或类似的东西。在大多数情况下,如果您正在设计公共图书馆,这是最方便的解决方案。