c# - 为什么编译器不能解析结果类型?

标签 c#

我有两个不同类型的 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>

这里显然分为三类:

  • ab是相同的。我们稍后再看。
  • cd是彼此的镜像。请注意,将哪个用作 this 并不重要。参数,就涉及类型推断而言。稍后更多...
  • e很简单:T被推断为 BaseClass以一种非常明显的方式

现在ab不工作因为BaseClass永远不会作为候选类型存在。根据我对类型推断算法的内存(它确实非常复杂),当某个类型不存在于任何参数类型中时,该类型永远不会被推断为类型参数。所以虽然BaseClassobject将是有效显式泛型类型参数,这些都不会被推断出来。

cd解决 T正在BaseClass ,因为有些推论要求从 IEnumerable<BaseClass> 进行转换至 IEnumerable<T> ,并且有一个来自 List<DerivedClass2> 的转换或 List<DerivedClass1> (分别为 cd )到 IEnumerable<T> .这仅适用于 T=BaseClass在考虑的类型之外,所以这就是推断的内容。

关于c# - 为什么编译器不能解析结果类型?,我们在Stack Overflow上找到一个类似的问题: https://stackoverflow.com/questions/53747384/

相关文章:

c# - 获取一组 HtmlInputRadioButton 控件的选中值

c# - 将多个枚举传递给方法并获取它们的值

C# HTML5 Websocket 服务器

c# - WPF 可检查菜单项中缺少复选标记

c# - 使用动态加载的 .Net 程序集进行二进制序列化

c# - SQLite:没有这样的表

c# - 在Unity3D中管理复杂的预制层级

c# - 在 C# 中运行代码行后,变量的值会发生变化吗?

c# - HTTP 错误 503。80 端口已经空闲时服务不可用

c# - 使用异步套接字的回调