简体   繁体   English

c#如何使用。包含在通用IEnumerable中<T>

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

I'm trying to create a generic method that I want to use for mixing a list of objects based off a property. 我正在尝试创建一个通用方法,该方法用于混合基于属性的对象列表。 Optionally, you can supply a list of list objects to merge some groups together. (可选)您可以提供列表对象列表,以将某些组合并在一起。 This works fine as a non-generic method and when I code the property manually, etc. 作为非泛型方法,以及当我手动编码属性等时,此方法都可以正常工作

Method head: 方法头:

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

Snippet of the methods body: 方法主体的代码段:

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();

As you can see, I'm having trouble using the contains method on a IEnumerable<T> as propertyInfo.GetValue(item, null) is returning an object. 如您所见,由于propertyInfo.GetValue(item, null)返回一个对象,我在IEnumerable<T>上使用contains方法遇到了麻烦。 I've tried various casting attempts and using .Any() instead, but I've hit a brick wall :( 我尝试了各种投射尝试,并改用.Any() ,但是我碰到了砖墙:(

Any help will be great, thanks! 任何帮助将是巨大的,谢谢!

Also, let me know if you need any more information but I guess, in essence, all I need to know is how to use .Contains() using <T> . 另外,如果您需要更多信息,请告诉我,但实际上,我想我仅需要知道如何使用<T>使用.Contains()

Set them to the same type? 将它们设置为相同类型? Custom IEqualityComparer ? 自定义IEqualityComparer

I've tried various casting attempts 我尝试了各种铸造尝试

Have you tried this one? 你尝试过这个吗?

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

Since ids is of type IGrouping<TKey, TElement> where TElement is of type T in your case, casting the value of property to T will allow for the comparison. 由于idsIGrouping<TKey, TElement>类型IGrouping<TKey, TElement>其中TElement在您的情况下为T类型,因此将属性值强制转换为T将允许进行比较。

Without seeing the whole method (because your type signatures clearly indicate it's not the whole method), here's an example implementation: 在没有看到整个方法的情况下(因为类型签名清楚地表明它不是整个方法),这是一个示例实现:

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();
    }
}

For the love of god stop passing property names and using reflection, the rest of Linq makes use of the gorgeous expression system, and you should too! 为了让上帝的爱停止传递属性名称和使用反射,Linq的其余部分使用了华丽的表达系统,您也应该这样做!

Ok, so I cracked it in the end. 好的,所以我最终破解了它。 I needed to add more detail in my generic method 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
    {
        ...

Note, I added class constraints which allows me to use nullable operators, etc. 注意,我添加了类约束,使我可以使用可为空的运算符,等等。

And the body became (after adding a propertyValue variable which is casted to the correct type!): 然后,主体变成了(添加了强制转换为正确类型的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();

声明:本站的技术帖子网页,遵循CC BY-SA 4.0协议,如果您需要转载,请注明本站网址或者原文地址。任何问题请咨询:yoyou2525@163.com.

 
粤ICP备18138465号  © 2020-2024 STACKOOM.COM