使用 Linq exceptBy 函数的泛型类中出现奇怪的编译器错误

Bob*_*lth 4 c# linq generics

我有以下代码(从我的生产代码中提取),应该可以编译,但不能:

    public class TestClass
    {
        public abstract class LocalizableEntity<TLocalizedEntity> where TLocalizedEntity : class, ILocalizedData, new()
        {
            public abstract string Id { get; set; }  // Primary key
            public abstract List<TLocalizedEntity> LocalizedData { get; set; }
        }

        public interface ILocalizedData
        {
            string CultureName { get; set; }
        }

        public void MergeData<TEntity, TLocalizedEntity>(IEnumerable<TEntity> newItems)
            where TEntity : LocalizableEntity<TLocalizedEntity>
            where TLocalizedEntity : class, ILocalizedData, new()
        {
            // Get the existing items
            var existingItems = new List<TEntity>();

            // Get the deleted items
            var deletedItems = existingItems
                .ExceptBy(newItems, x => x.Id, StringComparer.InvariantCultureIgnoreCase);
        }
    }
Run Code Online (Sandbox Code Playgroud)

问题是 LinqExceptBy函数在通用输入类型上被阻塞。正如所写,该行给了我一个编译器错误,突出显示ExceptBy并表示它无法从用法中推断出泛型类型参数。

'Enumerable.ExceptBy<TSource, TKey>(IEnumerable<TSource>, IEnumerable<TKey>, Func<TSource, TKey>, IEqualityComparer<TKey>?)'无法从用法推断方法的类型参数。尝试显式指定类型参数。

如果我修改有问题的语句以指定通用类型,我会收到不同的错误:

var deletedItems = existingItems
    .ExceptBy<TEntity, string>(newItems, x => x.Id, StringComparer.InvariantCultureIgnoreCase);
Run Code Online (Sandbox Code Playgroud)

现在编译器突出显示该existingItems变量并抱怨:

“List”不包含“ExceptBy”的定义,并且最佳扩展方法重载'Queryable.ExceptBy<TEntity, string>(IQueryable<TEntity>, IEnumerable<string>, Expression<Func<TEntity, string>>, IEqualityComparer<string>?)'需要“IQueryable”类型的接收器

好吧,我不知道这个错误消息中的“接收者”是什么,但它似乎在抱怨existingItems没有实现IQueryable,所以我.AsQueryable这样添加:

var deletedItems = existingItems
    .AsQueryable()
    .ExceptBy<TEntity, string>(newItems, x => x.Id, StringComparer.InvariantCultureIgnoreCase);
Run Code Online (Sandbox Code Playgroud)

现在它认为这newItems是一个IEnumerable<string>(而不是IEnumerable<TEntity>)并且它抱怨它无法从 转换IEnumerable<TEntity>IEnumerable<string>

我放弃。我是否做了一些严重错误的事情,或者编译器只是被复杂的泛型类型混淆了?

Cha*_*ace 6

ExceptBy期望第二个参数(this源可枚举之后的第一个参数)是键列表,而不是源对象的列表。

所以你需要以下内容

var deletedItems = existingItems
    .ExceptBy(newItems.Select(x => x.Id), x => x.Id, StringComparer.InvariantCultureIgnoreCase)
Run Code Online (Sandbox Code Playgroud)

点网小提琴

您可以编写一个扩展函数来执行相同的操作

public static IEnumerable<TSource> ExceptByKey<TSource, TKey> (
    this IEnumerable<TSource> first,
    IEnumerable<TSource> second,
    Func<TSource, TKey> keySelector,
    IEqualityComparer<TKey>? comparer = null)
  => first.ExceptBy(second.Select(keySelector), keySelector, comparer);
Run Code Online (Sandbox Code Playgroud)