我有一个专门的列表,其中包含以下类型的项目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获得学分;-)
(编辑反映.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.)
如果您使用的是.NET 3.5和LINQ:
Decimal result = myThingList.Max(i => i.Weight);
这将使得Min和Max的计算相当微不足道.
是的,您应该使用委托和匿名方法.
举个例子,请看这里.
基本上你需要实现类似于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
如果使用.NET 3.5,为什么不使用lambdas?
public Decimal GetMaximum(Funcprop) { Decimal result = Decimal.MinValue; foreach (IThing thing in this) result = Math.Max(result, prop(thing)); return result; }
用法:
Decimal result = list.GetMaximum(x => x.Weight);
这是强类型和有效的.还有一些扩展方法已经完成了这一点.