我有两个不同类型的 IEnumerable
,它们都派生自一个公共(public)基类。现在我尝试合并可枚举对象以获得基类的可枚举对象。我必须显式地将其中一个可枚举对象强制转换为基类才能使其正常工作。
我猜想编译器会自动为生成的可枚举类型选择最接近的公共(public)基类型,但事实并非如此。
这是该行为的示例:
namespace ConsoleApp1
{
public class BaseClass { }
public class DerivedClass1 : BaseClass { }
public class DerivedClass2 : BaseClass { }
class Program
{
static void Main(string[] args)
{
List<DerivedClass1> list1 = new List<DerivedClass1>();
List<DerivedClass2> list2 = new List<DerivedClass2>();
var a = list1.Union(list2); // Compiler Error
IEnumerable<BaseClass> b = list1.Union(list2); // Compiler Error
var c = list1.Cast<BaseClass>().Union(list2); // This works
var d = list1.Union(list2.Cast<BaseClass>()); // This works
var e = list1.Cast<BaseClass>().Union(list2.Cast<BaseClass>()); // This works, but ReSharper wants to remove one of the casts
}
}
}
var c
似乎很容易解释,因为第一个枚举现在是 BaseClass
类型,所以与第二个列表的联合,其中包含派生的元素来自 BaseClass 也很容易理解。
var d
对我来说不是那么容易理解,因为我们从 DerivedClass1
的可枚举开始并将其与 BaseClass
元素合并。我很惊讶这有效。是不是因为联合运算是一种交换运算,所以它必须像 c
一样工作?
最佳答案
值得记住的是Union
是一种扩展方法。这是您调用的方法签名:
public static IEnumerable<TSource> Union<TSource> (
this IEnumerable<TSource> first,
IEnumerable<TSource> second);
所以你的调用是有效的:
var a = Enumerable.Union(list1, list2);
IEnumerable<BaseClass> b = Enumerable.Union(list1, list2);
var c = Enumerable.Union(list1.Cast<BaseClass>(), list2);
var d = Enumerable.Union(list1, list2.Cast<BaseClass>());
var e = Enumerable.Union(list1.Cast<BaseClass>(), list2.Cast<BaseClass>());
这些调用中涉及的参数类型是:
a: List<DerivedClass1>, List<DerivedClass2>
b: List<DerivedClass1>, List<DerivedClass2> // Variable being assigned to doesn't matter
c: IEnumerable<BaseClass>, List<DerivedClass2>
d: List<DerivedClass1>, IEnumerable<BaseClass>
e: IEnumerable<BaseClass>, IEnumerable<BaseClass>
这里显然分为三类:
-
a
和b
是相同的。我们稍后再看。 -
c
和d
是彼此的镜像。请注意,将哪个用作this
并不重要。参数,就涉及类型推断而言。稍后更多... -
e
很简单:T
被推断为BaseClass
以一种非常明显的方式
现在a
和 b
不工作因为BaseClass
永远不会作为候选类型存在。根据我对类型推断算法的内存(它确实非常复杂),当某个类型不存在于任何参数类型中时,该类型永远不会被推断为类型参数。所以虽然BaseClass
和 object
将是有效显式泛型类型参数,这些都不会被推断出来。
c
和 d
解决 T
正在BaseClass
,因为有些推论要求从 IEnumerable<BaseClass>
进行转换至 IEnumerable<T>
,并且有一个来自 List<DerivedClass2>
的转换或 List<DerivedClass1>
(分别为 c
和 d
)到 IEnumerable<T>
.这仅适用于 T=BaseClass
在考虑的类型之外,所以这就是推断的内容。
关于c# - 为什么编译器不能解析结果类型?,我们在Stack Overflow上找到一个类似的问题: https://stackoverflow.com/questions/53747384/