C# 如何通过Lambda或LINQ从列表中获取不同的实例

C# 如何通过Lambda或LINQ从列表中获取不同的实例,c#,linq,.net-3.5,lambda,C#,Linq,.net 3.5,Lambda,我有一门课是这样的: class MyClass<T> { public string value1 { get; set; } public T objT { get; set; } } List<MyClass<T>> list; ... List<MyClass<T>> listDistinct = new List<MyClass<T>>(); foreach (MyClass<

我有一门课是这样的:

class MyClass<T> {
    public string value1 { get; set; }
    public T objT { get; set; }
}
List<MyClass<T>> list; 
...
List<MyClass<T>> listDistinct = new List<MyClass<T>>();
foreach (MyClass<T> instance in list)
{
    // some code to check if listDistinct does contain obj with intance.Value1
    // then listDistinct.Add(instance);
}
List<MyClass<T>> list; 
...
List<MyClass<T>> listDistinct = list.Distinct(new MyClassComparer<T>).ToList();
var listDistinct
    = list.GroupBy(
        i => i.value1,
        (key, group) => group.First()
    ).ToArray();
class-MyClass{
公共字符串值1{get;set;}
公共T对象{get;set;}
}
和这个班级的名单。我想使用.net 3.5 lambda或linq获得一个MyClass的列表(按不同的值1)。我想这是可能的,而且比.net 2.0中缓存如下列表的方法简单得多:

class MyClass<T> {
    public string value1 { get; set; }
    public T objT { get; set; }
}
List<MyClass<T>> list; 
...
List<MyClass<T>> listDistinct = new List<MyClass<T>>();
foreach (MyClass<T> instance in list)
{
    // some code to check if listDistinct does contain obj with intance.Value1
    // then listDistinct.Add(instance);
}
List<MyClass<T>> list; 
...
List<MyClass<T>> listDistinct = list.Distinct(new MyClassComparer<T>).ToList();
var listDistinct
    = list.GroupBy(
        i => i.value1,
        (key, group) => group.First()
    ).ToArray();
列表;
...
List listDistinct=新列表();
foreach(列表中的MyClass实例)
{
//一些代码用于检查listDistinct是否包含具有intance.Value1的obj
//然后listDistinct.Add(实例);
}

lambda或LINQ的方法是什么?

嗯。。。我可能会编写一个自定义的
IEqualityComparer
,以便使用:

var listDistinct = list.Distinct(comparer).ToList();
并通过LINQ编写比较器

可能有点过度,但至少可重复使用:

首先使用:

static class Program {
    static void Main() {
        var data = new[] {
            new { Foo = 1,Bar = "a"}, new { Foo = 2,Bar = "b"}, new {Foo = 1, Bar = "c"}
        };
        foreach (var item in data.DistinctBy(x => x.Foo))
            Console.WriteLine(item.Bar);
        }
    }
}
使用实用方法:

public static class ProjectionComparer
{
    public static IEnumerable<TSource> DistinctBy<TSource,TValue>(
        this IEnumerable<TSource> source,
        Func<TSource, TValue> selector)
    {
        var comparer = ProjectionComparer<TSource>.CompareBy<TValue>(
            selector, EqualityComparer<TValue>.Default);
        return new HashSet<TSource>(source, comparer);
    }
}
public static class ProjectionComparer<TSource>
{
    public static IEqualityComparer<TSource> CompareBy<TValue>(
        Func<TSource, TValue> selector)
    {
        return CompareBy<TValue>(selector, EqualityComparer<TValue>.Default);
    }
    public static IEqualityComparer<TSource> CompareBy<TValue>(
        Func<TSource, TValue> selector,
        IEqualityComparer<TValue> comparer)
    {
        return new ComparerImpl<TValue>(selector, comparer);
    }
    sealed class ComparerImpl<TValue> : IEqualityComparer<TSource>
    {
        private readonly Func<TSource, TValue> selector;
        private readonly IEqualityComparer<TValue> comparer;
        public ComparerImpl(
            Func<TSource, TValue> selector,
            IEqualityComparer<TValue> comparer)
        {
            if (selector == null) throw new ArgumentNullException("selector");
            if (comparer == null) throw new ArgumentNullException("comparer");
            this.selector = selector;
            this.comparer = comparer;
        }

        bool IEqualityComparer<TSource>.Equals(TSource x, TSource y)
        {
            if (x == null && y == null) return true;
            if (x == null || y == null) return false;
            return comparer.Equals(selector(x), selector(y));
        }

        int IEqualityComparer<TSource>.GetHashCode(TSource obj)
        {
            return obj == null ? 0 : comparer.GetHashCode(selector(obj));
        }
    }
}
公共静态类项目比较程序
{
公共静态IEnumerable DistinctBy(
这是一个数不清的来源,
Func选择器)
{
var comparer=projectoncomparer.CompareBy(
选择器,EqualityComparer.Default);
返回新的哈希集(源、比较器);
}
}
公共静态类ProjectOnComparer
{
公共静态IEqualityComparer Comparer比(
Func选择器)
{
返回CompareBy(选择器,EqualityComparer.Default);
}
公共静态IEqualityComparer Comparer比(
函数选择器,
IEqualityComparer(比较器)
{
返回新的比较器mpl(选择器、比较器);
}
密封类比较器示例:IEqualityComparer
{
专用只读函数选择器;
专用只读IEqualityComparer比较器;
公共比例尺(
函数选择器,
IEqualityComparer(比较器)
{
如果(选择器==null)抛出新的ArgumentNullException(“选择器”);
如果(comparer==null)抛出新的ArgumentNullException(“comparer”);
this.selector=选择器;
this.comparer=比较器;
}
布尔IEqualityComparer.Equals(t源x,t源y)
{
如果(x==null&&y==null)返回true;
如果(x==null | | y==null)返回false;
返回比较器.Equals(选择器(x),选择器(y));
}
int IEqualityComparer.GetHashCode(TSource obj)
{
返回obj==null?0:comparer.GetHashCode(选择器(obj));
}
}
}
签出,可接受IEqualityComparer:

class MyClassComparer<T> : IEqualityComparer<MyClass<T>>
{
    // Products are equal if their names and product numbers are equal.
    public bool Equals(MyClass<T> x, MyClass<T>y)
    {
        // Check whether the compared objects reference the same data.
        if (Object.ReferenceEquals(x, y)) return true;

        // Check whether any of the compared objects is null.
        if (Object.ReferenceEquals(x, null) || Object.ReferenceEquals(y, null))
            return false;

        // Check whether the products' properties are equal.
        return x.value1 == y.value1;
    }

    // If Equals() returns true for a pair of objects,
    // GetHashCode must return the same value for these objects.

    public int GetHashCode(MyClass<T> x)
    {
        // Check whether the object is null.
        if (Object.ReferenceEquals(x, null)) return 0;

        // Get the hash code for the Name field if it is not null.
        return (x.value1 ?? "").GetHashCode();
    }
}
类MyClassComparer:IEqualityComparer
{
//如果名称和产品编号相等,则产品相等。
公共布尔等于(MyClass x,MyClassy)
{
//检查比较对象是否引用相同的数据。
if(Object.ReferenceEquals(x,y))返回true;
//检查是否有任何比较对象为空。
if(Object.ReferenceEquals(x,null)| | Object.ReferenceEquals(y,null))
返回false;
//检查产品的属性是否相等。
返回x.value1==y.value1;
}
//如果Equals()对一对对象返回true,
//GetHashCode必须为这些对象返回相同的值。
公共int GetHashCode(MyClass x)
{
//检查对象是否为空。
if(Object.ReferenceEquals(x,null))返回0;
//如果名称字段不为null,则获取其哈希代码。
返回(x.value1??).GetHashCode();
}
}
您的代码段可能如下所示:

class MyClass<T> {
    public string value1 { get; set; }
    public T objT { get; set; }
}
List<MyClass<T>> list; 
...
List<MyClass<T>> listDistinct = new List<MyClass<T>>();
foreach (MyClass<T> instance in list)
{
    // some code to check if listDistinct does contain obj with intance.Value1
    // then listDistinct.Add(instance);
}
List<MyClass<T>> list; 
...
List<MyClass<T>> listDistinct = list.Distinct(new MyClassComparer<T>).ToList();
var listDistinct
    = list.GroupBy(
        i => i.value1,
        (key, group) => group.First()
    ).ToArray();
列表;
...
List listDistinct=List.Distinct(新的MyClassComparer.ToList();

马克和达尔贝克的答案似乎都很有效。不过,我有一个更简单的解决方案。您可以使用
GroupBy
,而不是使用
Distinct
。事情是这样的:

class MyClass<T> {
    public string value1 { get; set; }
    public T objT { get; set; }
}
List<MyClass<T>> list; 
...
List<MyClass<T>> listDistinct = new List<MyClass<T>>();
foreach (MyClass<T> instance in list)
{
    // some code to check if listDistinct does contain obj with intance.Value1
    // then listDistinct.Add(instance);
}
List<MyClass<T>> list; 
...
List<MyClass<T>> listDistinct = list.Distinct(new MyClassComparer<T>).ToList();
var listDistinct
    = list.GroupBy(
        i => i.value1,
        (key, group) => group.First()
    ).ToArray();
请注意,我已将两个函数传递给
GroupBy()
。第一个是键选择器。第二组只从每组中获取一项。根据你的问题,我认为
First()
是正确的答案。如果你愿意,你可以写一个不同的。你可以试试
Last()
,看看我的意思

我使用以下输入运行了一个测试:

var list = new [] {
    new { value1 = "ABC", objT = 0 },
    new { value1 = "ABC", objT = 1 },
    new { value1 = "123", objT = 2 },
    new { value1 = "123", objT = 3 },
    new { value1 = "FOO", objT = 4 },
    new { value1 = "BAR", objT = 5 },
    new { value1 = "BAR", objT = 6 },
    new { value1 = "BAR", objT = 7 },
    new { value1 = "UGH", objT = 8 },
};
结果是:

//{ value1 = ABC, objT = 0 }
//{ value1 = 123, objT = 2 }
//{ value1 = FOO, objT = 4 }
//{ value1 = BAR, objT = 5 }
//{ value1 = UGH, objT = 8 }

我还没有测试它的性能。我相信这个解决方案可能比使用
Distinct
的解决方案慢一点。尽管存在这一缺点,但有两大优点:简单性和灵活性。通常,简单优于优化,但这实际上取决于您试图解决的问题。

您可以使用以下扩展方法:

    IEnumerable<MyClass> distinctList = sourceList.DistinctBy(x => x.value1);

    public static IEnumerable<TSource> DistinctBy<TSource, TKey>(
        this IEnumerable<TSource> source,
        Func<TSource, TKey> keySelector)
    {
        var knownKeys = new HashSet<TKey>();
        return source.Where(element => knownKeys.Add(keySelector(element)));
    }
IEnumerable distinctList=sourceList.DistinctBy(x=>x.value1);
公共静态IEnumerable DistinctBy(
这是一个数不清的来源,
Func键选择器)
{
var knownKeys=新的HashSet();
返回source.Where(element=>knownKeys.Add(keySelector(element));
}

我接受了Marc的答案,将其修复为将TSource作为值类型使用(默认测试(TSource)而不是null),清理了一些冗余的类型规范,并为其编写了一些测试。这是我今天使用的。感谢Marc的伟大构想和实施

public static class LINQExtensions
{
    public static IEnumerable<TSource> DistinctBy<TSource, TValue>(
        this IEnumerable<TSource> source,
        Func<TSource, TValue> selector)
    {
        var comparer = ProjectionComparer<TSource>.CompareBy(
            selector, EqualityComparer<TValue>.Default);
        return new HashSet<TSource>(source, comparer);
    }
}
public static class ProjectionComparer<TSource>
{
    public static IEqualityComparer<TSource> CompareBy<TValue>(
        Func<TSource, TValue> selector)
    {
        return CompareBy(selector, EqualityComparer<TValue>.Default);
    }
    public static IEqualityComparer<TSource> CompareBy<TValue>(
        Func<TSource, TValue> selector,
        IEqualityComparer<TValue> comparer)
    {
        return new ComparerImpl<TValue>(selector, comparer);
    }
    sealed class ComparerImpl<TValue> : IEqualityComparer<TSource>
    {
        private readonly Func<TSource, TValue> _selector;
        private readonly IEqualityComparer<TValue> _comparer;
        public ComparerImpl(
            Func<TSource, TValue> selector,
            IEqualityComparer<TValue> comparer)
        {
            if (selector == null) throw new ArgumentNullException("selector");
            if (comparer == null) throw new ArgumentNullException("comparer");
            _selector = selector;
            _comparer = comparer;
        }

        bool IEqualityComparer<TSource>.Equals(TSource x, TSource y)
        {
            if (x.Equals(default(TSource)) && y.Equals(default(TSource)))
            {
                return true;
            }

            if (x.Equals(default(TSource)) || y.Equals(default(TSource)))
            {
                return false;
            }
            return _comparer.Equals(_selector(x), _selector(y));
        }

        int IEqualityComparer<TSource>.GetHashCode(TSource obj)
        {
            return obj.Equals(default(TSource)) ? 0 : _comparer.GetHashCode(_selector(obj));
        }
    }
}

这将更简单

var distinctList = list.GroupBy(l => l.value1, (key, c) => l.FirstOrDefault());

在linq,这对团队来说是更大的进步

list.GroupBy(li => li.value, (key, grp) => li.FirstOrDefault());

关于代码的一个问题:什么是ProjectOnComparer?一个.Net类或LINQ或IEnumerable相关类,以便您可以自定义扩展?好的。我认为“ProjectOnComparer”是您定义的任何类名,但在该类中,您已将扩展方法DistinctBy()自定义为IEnumerable,而ProjectOnComparer是另一个助手类,对吗?ProjectionComparer可以是不同的名称而不是相同的名称吗?如果我想得到MyClass的value1列表,我可以像这样使用这个比较器:list listValue1s=list.Distinct(comparer.ToList()。Select(y=>y.value1);是吗?ProjectonComparer的名字不重要-你可以