当前位置:  开发笔记 > 编程语言 > 正文

C#:获取列表中所有项的任意属性的最大值和最小值

如何解决《C#:获取列表中所有项的任意属性的最大值和最小值》经验,为你挑选了4个好方法。

我有一个专门的列表,其中包含以下类型的项目IThing:

public class ThingList : IList
{...}

public interface IThing
{
    Decimal Weight { get; set; }
    Decimal Velocity { get; set; }
    Decimal Distance { get; set; }
    Decimal Age { get; set; }
    Decimal AnotherValue { get; set; }

    [...even more properties and methods...]
}

有时我需要知道列表中所有内容的某个属性的最大值或最小值.因为"告诉不要问"我们让列表弄明白:

public class ThingList : IList
{
    public Decimal GetMaximumWeight()
    {
        Decimal result = 0;
        foreach (IThing thing in this) {
            result = Math.Max(result, thing.Weight);
        }
        return result;
    }
}

这是非常好的.但有时我需要最小重量,有时最大速度等等.我不希望GetMaximum*()/GetMinimum*()每一个属性都有一对.

一种解决方案是反思.像(抓住你的鼻子,强烈的代码味道!):

Decimal GetMaximum(String propertyName);
Decimal GetMinimum(String propertyName);

有没有更好,更少臭的方法来实现这一目标?

谢谢,埃里克

编辑:@Matt:.Net 2.0

结论:.Net 2.0(使用Visual Studio 2005)没有更好的方法.也许我们应该很快转移到.Net 3.5和Visual Studio 2008.多谢你们.

结论:有不同的方法远比反思好.取决于运行时和C#版本.看看Jon Skeets对差异的回答.所有答案都非常有用.

我将采用Sklivvz建议(匿名方法).其他人(Konrad Rudolph,Matt Hamilton和Coincoin)有几个代码片段,它们实现了Sklivvz的想法.不幸的是,我只能"接受"一个答案.

非常感谢你.你们都可以感受到"接受",尽管只有Sklivvz获得学分;-)



1> Jon Skeet..:

(编辑反映.NET 2.0的答案,以及VS2005中的LINQBridge ...)

这里有三种情况 - 虽然OP只有.NET 2.0,但其他遇到同样问题的人可能不会......

1)使用.NET 3.5和C#3.0:像这样使用LINQ to Objects:

decimal maxWeight = list.Max(thing => thing.Weight);
decimal minWeight = list.Min(thing => thing.Weight);

2)使用.NET 2.0和C#3.0:使用LINQBridge和相同的代码

3)使用.NET 2.0和C#2.0:使用LINQBridge和匿名方法:

decimal maxWeight = Enumerable.Max(list, delegate(IThing thing) 
    { return thing.Weight; }
);
decimal minWeight = Enumerable.Min(list, delegate(IThing thing)
    { return thing.Weight; }
);

(我没有C#2.0编译器来测试上面的内容 - 如果它抱怨模糊的转换,请将委托转换为Func .)

LINQBridge将与VS2005一起使用,但是你没有获得扩展方法,lambda表达式,查询表达式等.显然,迁移到C#3是一个更好的选择,但我更喜欢使用LINQBridge自己实现相同的功能.

如果您需要同时获得最大值和最小值,所有这些建议都涉及将列表移动两次.如果你有一种情况,你从懒惰的磁盘或类似的东西加载,并且你想一次性计算几个聚合,你可能想看看我在MiscUtil中的"推送LINQ"代码.(也适用于.NET 2.0.)



2> Matt Hamilto..:

如果您使用的是.NET 3.5和LINQ:

Decimal result = myThingList.Max(i => i.Weight);

这将使得Min和Max的计算相当微不足道.



3> Sklivvz..:

是的,您应该使用委托和匿名方法.

举个例子,请看这里.

基本上你需要实现类似于List的Find方法的东西.

这是一个示例实现

public class Thing
{
    public int theInt;
    public char theChar;
    public DateTime theDateTime;

    public Thing(int theInt, char theChar, DateTime theDateTime)
    {
        this.theInt = theInt;
        this.theChar = theChar;
        this.theDateTime = theDateTime;
    }

    public string Dump()
    {
        return string.Format("I: {0}, S: {1}, D: {2}", 
            theInt, theChar, theDateTime);
    }
}

public class ThingCollection: List
{
    public delegate Thing AggregateFunction(Thing Best, 
                        Thing Candidate);

    public Thing Aggregate(Thing Seed, AggregateFunction Func)
    {
        Thing res = Seed;
        foreach (Thing t in this) 
        {
            res = Func(res, t);
        }
        return res;
    }
}

class MainClass
{
    public static void Main(string[] args)
    {
        Thing a = new Thing(1,'z',DateTime.Now);
        Thing b = new Thing(2,'y',DateTime.Now.AddDays(1));
        Thing c = new Thing(3,'x',DateTime.Now.AddDays(-1));
        Thing d = new Thing(4,'w',DateTime.Now.AddDays(2));
        Thing e = new Thing(5,'v',DateTime.Now.AddDays(-2));

        ThingCollection tc = new ThingCollection();

        tc.AddRange(new Thing[]{a,b,c,d,e});

        Thing result;

        //Max by date
        result = tc.Aggregate(tc[0], 
            delegate (Thing Best, Thing Candidate) 
            { 
                return (Candidate.theDateTime.CompareTo(
                    Best.theDateTime) > 0) ? 
                    Candidate : 
                    Best;  
            }
        );
        Console.WriteLine("Max by date: {0}", result.Dump());

        //Min by char
        result = tc.Aggregate(tc[0], 
            delegate (Thing Best, Thing Candidate) 
            { 
                return (Candidate.theChar < Best.theChar) ? 
                    Candidate : 
                    Best; 
            }
        );
        Console.WriteLine("Min by char: {0}", result.Dump());               
    }
}

结果:

Max by date: I: 4, S: w, D: 10/3/2008 12:44:07 AM
Min by char: I: 5, S: v, D: 9/29/2008 12:44:07 AM



4> Konrad Rudol..:

如果使用.NET 3.5,为什么不使用lambdas?

public Decimal GetMaximum(Func prop) {
    Decimal result = Decimal.MinValue;
    foreach (IThing thing in this)
        result = Math.Max(result, prop(thing));

    return result;
}

用法:

Decimal result = list.GetMaximum(x => x.Weight);

这是强类型和有效的.还有一些扩展方法已经完成了这一点.

推荐阅读
雨天是最美
这个屌丝很懒,什么也没留下!
DevBox开发工具箱 | 专业的在线开发工具网站    京公网安备 11010802040832号  |  京ICP备19059560号-6
Copyright © 1998 - 2020 DevBox.CN. All Rights Reserved devBox.cn 开发工具箱 版权所有