我有两种方法:
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>());
调用错误的方法(第一种方法)。
我怎样才能让它发挥作用?
答案 0 :(得分:5)
如果存在可以以相同方式隐式调用的泛型重载,则必须使用显式调用。
此代码将调用第二个重载。
connection.Insert<Foo>(new List<Foo>());
答案 1 :(得分:2)
这个原型:
public static int Insert<T>(this System.Data.IDbConnection connection, T param)
...几乎可以接受任何param
,因为它没有类型限制。它会接受Foo
,IList<Foo>
,List<Foo>
以及非Foo
的所有内容。因此,它与第二个原型重叠,这个问题称为收敛。
如果可能的话,最好避免整个混乱。如果可以,可以更狭义地定义第一个原型,如下所示:
public static int Insert<T>(this IDbConnection connection, T param) where T: Foo
然后只有当T
是Foo
或Foo
的后代时才会调用它。 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
或类似的东西。在大多数情况下,如果您正在设计公共图书馆,这是最方便的解决方案。