泛型方法处理 IEnumerable 的方式与泛型类型不同
本文关键字:泛型类型 方式 处理 IEnumerable 泛型方法 | 更新日期: 2023-09-27 18:27:09
请检查以下代码段:
public interface ICountable { }
public class Counter<T>
where T : ICountable
{
public int Count(IEnumerable<T> items)
{
return 0;
}
public int Count(T Item)
{
return 0;
}
}
public class Counter
{
public int Count<T>(IEnumerable<T> items)
where T : ICountable
{
return 0;
}
public int Count<T>(T Item)
where T : ICountable
{
return 0;
}
}
计数器的两个版本仅在泛型参数的规范上有所不同。其中一个定义为泛型类型参数,另一个定义为泛型参数。两者都限制实现 ICountable 接口的方法参数。我将分别称它们为特定和非特定。
现在,我正在定义一个实现 ICountable 接口的类和一个实例集合:
public class CItem : ICountable { }
var countables = new List<CItem>();
然后,我想在集合上使用两个计数器类。
var specific = new Counter<CItem>();
var nonspecific = new Counter();
specific.Count(countables);
nonspecific.Count(countables);
特定计数器识别可计数集合应属于签名 int Count(IEnumerable(,但非特定版本不会。我收到错误:
类型"
System.Collections.Generic.List<CItem>
"不能用作 泛型类型或方法中的类型参数 'T
' 'Counter.Count<T>(T)
'。没有隐式引用转换List<CItem>
' 到ICountable
.
似乎非特定版本对集合使用了错误的签名。
为什么它们的行为不同?如何指定非特定版本以使其行为与另一个版本相同?
注意:我知道这个例子是不现实的。但是,我在扩展方法的非常复杂的情况下遇到了这个问题。为了简单起见,我使用这些类
提前致谢
非特定类的问题在于编译器在编译时不知道 T 类型,这就是为什么它无法为方法 Count<T>()
选择正确的重载。但是,如果您设置泛型类型约束,编译器现在知道需要什么类型...
如果你要注释掉带有签名的方法public int Count<T>(T Item)
它将编译,因为它将使用具有正确签名的方法(这是public int Count<T>(IEnumerable<T> items)
如果您帮助编译器通过将 List 显式转换为IEnumerable<CItem>
来推断类型,它也会编译和运行:
nonspecific.Count(countables as IEnumerable<CItem>);
看看简化的场景:
static string A<T>(IEnumerable<T> collection)
{
return "method for ienumerable";
}
static string A<T>(T item)
{
return "method for single element";
}
static void Main(string[] args)
{
List<int> numbers = new List<int>() { 5, 3, 7 };
Console.WriteLine(A(numbers));
}
输出:"单元素的方法">
没记错的话(将尝试在规范中找到引用(,选择T
方法,因为它与类型完全匹配。
类型推断正确标识了两种泛型方法都适用,如Count<CItem>(IEnumerable<CItem> items)
和Count<List<CItem>>(List<CItem> items)
。但是,第一个在重载分辨率中丢失,因为第二个更具体。约束仅在此之后发挥作用,因此会出现编译时错误。
如果您countables
使用
IEnumerable<CItem> countables = new List<CItem>();
然后选择变得Count<CItem>(IEnumerable<CItem> items)
和Count<IEnumerable<CItem>>(IEnumerable<CItem> items)
,第一个赢得过载分辨率。
,编译器之所以认为你调用的是Counter.Count(T(而不是Counter.Count
我认为最好将采用 IEnumerble 作为参数的方法名称更改为类似 CountAll 的方法名称。.NET 框架为 List.Remove 和 List.RemoveAll 所做的一些事情。最好使代码更具体,而不是让编译器执行所有决策。