当我在 linq 中使用具有基本类型的 lambda 创建器作为"Where"条件时出现编译错误



我有一些代码。这不是真正的代码,但与我在生产中的问题非常相似。 第 2 点中未编译的ASupplier调用var filtered导致List<IA>。它看起来像是有效原因Where声明为

public static IEnumerable<TSource> Where<TSource>(this IEnumerable<TSource> source, Func<TSource, bool> predicate)

但我不明白为什么第 3 点有效,因为 FixedIACondition 声明与调用的结果相似IAConditionCreator

using System;
using System.Collections.Generic;
using System.Linq;
namespace LinqWhereConditionProblem
{
    internal class Program
    {
        public static void Main(string[] args)
        {
            var collection = new List<A>();
            ASupplier(collection); // 1) OK
            var filtered = collection.Where(IAConditionCreator("a")).ToList();
            ASupplier(filtered); // 2) NOT OK
            var filtered2 = collection.Where(FixedIACondition).ToList();
            ASupplier(filtered2); // 3) OK
        }
        private static void ASupplier(IReadOnlyCollection<A> aCollection)
        {
            foreach (var a in aCollection)
            {
                Console.WriteLine(a.GetText());
            }
        }
        private static Func<IA, bool> IAConditionCreator(string value)
        {
            return a => a.GetText() == value;
        }
        private static bool FixedIACondition(IA ia) => ia.GetText() == "aa";
    }
    public interface IA
    {
        string GetText();
    }
    public class A : IA
    {
        public string GetText()
        {
            return "ABC";
        }
    }
}

编译器执行隐式类型转换的方式对于委托对象和方法组是不同的,我想它的主要目标是尽可能减少类型转换(包括隐式类型转换(的数量。

方法 FixedIACondition 作为编译器无论如何都必须转换为委托对象的方法组引用。编译能够推断出这个方法组可以转换成一个Func<A, bool>,这是.Where所需要的类型。

对于IAConditionCreator,编译器已经有一个委托对象,现在尝试将该对象放入调用中。但是,它必须将集合转换为IEnumerable<IA>或将委托转换为Func<A, bool>。在 .NET 中对协变委托的支持不是很好(尝试调用 Delegate.Combine(,我想编译器团队已经意识到了这一点,因此试图避免这种情况,而是进行第一次转换。

更改方法ASupplier以接收 IASupplier 可解决此问题:

private static void ASupplier(IReadOnlyCollection<IA> aCollection)
{
    foreach (var a in aCollection)
    {
        Console.WriteLine(a.GetText());
    }
}

如果您只关心界面,这可能是您想要的。如果 Aupplier 仅用于处理类型 A,则可以从列表中强制转换或过滤那些 OfType A

ASupplier(filtered.OfType<A>().ToList()); // this will filter the list to return those of type A
ASupplier(filtered.Cast<A>().ToList()); // this will throw exception if any object can't be cast to A

另一种选择是强制转换 Func 本身以返回所需的类型:

var condition = (Func<A, bool>)IAConditionCreator("a");
var filtered = collection.Where(condition).ToList();
ASupplier(filtered);

还有其他方法可以处理多态性,但随后必须更具体地了解方案。

最新更新