如何检查每个列表成员的属性值是否相同

15

假设我有一个名为 Student 的类,其中有一个属性 int Age。现在如果我有一个 List<Student> students,如何检查列表中所有学生的年龄是否相等?


1
请参见 https://dev59.com/KW035IYBdhLWcg3wVul1。 - goodeye
6个回答

32

您可以通过使用All方法来检查,假设您的列表具有学生:

var firstStudent = students.First();
students.All(s => s.Age == firstStudent.Age);

3
尽管可能会更好将“First”先提起,但原文意思不应改变。 - Marc Gravell
你的第一种实现并不糟糕,因为枚举到第一个元素并不昂贵。 - Alex Siepman
通常情况下,当您可以在一次调用中完成相同的操作时,我会尽量避免两次查询序列... - Rawling
1
@Sam:我认为是一样的。 - cuongle
1
@CuongLe 是的,它们似乎都短路了(Any vs All),而在这种情况下,All更容易阅读。 - Sam
显示剩余2条评论

11

如果您希望在一次查询中完成此操作,而不是两次(这通常是不良做法),

bool allAgesAreTheSame = (students.Select(s => s.Age).Distinct().Count() < 2);

它也会在你根本没有学生的情况下返回真值,而不是抛出异常。(你可以用 == 1 来代替 < 2 来在这种琐碎的情况下返回假值。)


1
一个有趣的想法:你可以在Distinct()Count()之间添加Take(2),这将允许一定程度的短路 - 但是,它会增加额外的迭代器开销 - 因此,这是否是一个好事取决于大量不同年龄是否可能发生。 - Marc Gravell
@Marc 很好的观点,我对自己使用 Count 而不是只需要 "0、1 或 2" 感到非常失望,通常我很擅长发现这种问题。 - Rawling
1
这将迭代所有年龄,即使前两个年龄不同。 - Alex Siepman
1
@Alex,Marc已经指出了这一点,并提供了一个解决方案和一个警告,说明为什么您可能不想使用该解决方案,因此我将解决方案留在他的评论中而不是复制到我的答案中。相比于两个查询的解决方案,我仍然会选择这个。 - Rawling

4

仅供参考-实际中不确定是否采用这种方式,但它将非常有效:

  • 如果可用鸭子类型,则使用类型化迭代器(与LINQ不同,它不会)-请注意List<T>确实提供鸭子类型的迭代器
  • 无重复迭代
  • 空序列无失败等
  • 无委托,捕获环境等
  • 等等

代码:

using(var iter = students.GetEnumerator()) // a List<T>.Enumerator struct
{
    if(!iter.MoveNext()) return true; // or false, as you define for "empty"

    int age = iter.Current.Age;
    while(iter.MoveNext())
        if(iter.Current.Age != age)
            return false;
    return true;
}

1
@Rawling 我是老派的人,就是这样 ;p - Marc Gravell

3
如果学生可以没有任何元素,你可以这样做:
var firstStudent = students.FirstOrDefault();
var areSame =students.All(s => s.Age == firstStudent.Age);

2
@Rawling 我测试了这个解决方案,它没有抛出异常,因为如果元素数量为0,All()方法将返回true而不尝试执行lambda谓词! - Alex Siepman

0

如果您只需要检查一次,那么标记的答案是最佳解决方案。如果您需要在代码中多次使用它,请编写一个静态扩展来检查属性的相等性:

public static bool GetIdenticProperty<TSource, TKey>(this IEnumerable<TSource> source, Func<TSource, TKey> predicate)
{
    IEnumerable<TSource> enumerable = source as IList<TSource> ?? source.ToList();
    if ((!enumerable.Any()) || (enumerable.Count() == 1))
        return true; // or false if you prefere
    var firstItem = enumerable.First();
    bool allSame = enumerable.All(i => Equals(predicate(i), predicate(firstItem)));
    return allSame;
}

如果您想在以后使用属性的值,请返回该值:
public static TKey GetIdenticProperty<TSource, TKey>(this IEnumerable<TSource> source, Func<TSource, TKey> predicate)
{
    IEnumerable<TSource> enumerable = source as IList<TSource> ?? source.ToList();
    if (!enumerable.Any())
        return default(TKey);
    var firstItem = enumerable.First();
    bool allSame = enumerable.All(i => Equals(predicate(i), predicate(firstItem)));
    return allSame ? predicate(firstItem) : default(TKey);
}

但是使用这段代码时,您需要检查返回值是否与属性类型相关的nulldefault(TKey)


0

如果您无法使用linq,您始终可以循环遍历所有学生:

private bool sameAge (List<Student> students)
{
    int auxAge = students.Count > 0? students[0].Age : 0;

    foreach (Student stu in students)
    {
        if (stu.Age != auxAge) 
            return false;
    }
    return true;
}

网页内容由stack overflow 提供, 点击上面的
可以查看英文原文,
原文链接