c# 如何在通用 IEnumerable<T> 上使用 .Contains

c# How To Use .Contains on Generic IEnumerable<T>

我正在尝试创建一个通用方法,我想用它来混合基于 属性 的对象列表。或者,您可以提供列表对象列表以将一些组合并在一起。这作为一种非通用方法工作得很好,当我手动编码 属性 等

方法头:

public static IEnumerable<T> MixObjectsByProperty<T>(
    IEnumerable<T> objects,
    string propertyName,
    IEnumerable<IEnumerable<T>> groupsToMergeByProperty = null)

方法正文片段:

groups =
    (from item in objects
    let propertyInfo = item.GetType().GetProperty(propertyName)
    where propertyInfo != null
    group item by groupsToMergeByProperty
        .FirstOrDefault(ids => ids.Contains(propertyInfo.GetValue(item, null)))
        //.FirstOrDefault(ids => ids.Any(propertyInfo.GetValue(item, null)))
        ?.First()
        ?? propertyInfo.GetValue(item, null)
    into itemGroup
    select itemGroup.ToArray())
    .ToList();

如您所见,我在 IEnumerable<T> 上使用 contains 方法时遇到问题,因为 propertyInfo.GetValue(item, null) 正在返回一个对象。我尝试了各种铸造尝试并使用 .Any() 代替,但我遇到了障碍:(

任何帮助都会很棒,谢谢!

此外,如果您需要更多信息,请告诉我,但我想,从本质上讲,我需要知道的是如何使用 .Contains() 使用 <T>

将它们设置为同一类型?自定义 IEqualityComparer?

I've tried various casting attempts

你试过这个吗?

.FirstOrDefault(ids => ids.Contains((T)propertyInfo.GetValue(item, null)))

由于 idsIGrouping<TKey, TElement> 类型,其中 TElementT 类型,因此将 属性 的值转换为 T 将允许进行比较。

好的,所以我最终还是破解了。我需要在我的通用方法 header/signature.

中添加更多细节
    public static IEnumerable<T> MixObjectsByProperty<T, U>(
        IEnumerable<T> objects, string propertyName, IEnumerable<IEnumerable<U>> groupsToMergeByProperty = null)
        where T : class
        where U : class
    {
        ...

注意,我添加了 class 约束,允许我使用可空运算符等。

正文变成了(在添加了一个转换为正确类型的 propertyValue 变量之后!):

            groups =
                (from item in objects
                 let propertyInfo = item.GetType().GetProperty(propertyName)
                 where propertyInfo != null
                 let propertyValue = (U)propertyInfo.GetValue(item, null)
                 group item by
                     groupsToMergeByProperty
                         .FirstOrDefault(ids => ids.Contains(propertyValue))
                         ?.First()
                     ?? propertyValue
                 into itemGroup
                 select itemGroup.ToArray())
                .ToList();

没有看到整个方法(因为您的类型签名清楚地表明它不是整个方法),这是一个示例实现:

public class Ext
{
    public static List<T[]> MixObjectsByProperty<T, TProp, U>(
        IEnumerable<T> source,
        Expression<Func<T, TProp>> property,
        IEnumerable<IEnumerable<U>> groupsToMix = null)
        where T : class
        where U : TProp
    {
        var prop = (PropertyInfo)(property.Body as MemberExpression)?.Member;
        if (prop == null) throw new ArgumentException("Couldn't determine property");

        var accessor = property.Compile();

        var groups = 
            from item in source
            let value = (U)accessor(item)
            group item by
                groupsToMix.FirstOrDefault((ids => ids.Contains(value)))
            into itemGroup
            select itemGroup.ToArray();
        return groups.ToList();
    }
}

看在上帝的份上 停止传递 属性 名称并使用反射,Linq 的其余部分使用华丽的表达系统,您也应该这样做!