others - C#: 在 List 中,获取所有项目的任意属性的最大值和最小值

我有一个特殊的列表,包含IThing类型的项:


public class ThingList : IList<IThing>
{...}

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<IThing>
{
 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);

有没有更好的方法来完成这个任务?

时间:

有的,你应该使用委托和匿名方法。

有关例子,请参见这里

基本上,你需要实现类似于查找List的方法 。

下面是例子实现


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<Thing>
{
	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

1)使用NET 35和C30: 使用像这样的LINQ对象:


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

2)使用NET 20和C30: 使用linQBridge和相同的代码

3)使用NET 20和C20: 使用linQBridge和匿名方法:


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

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


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

这就使得最小和最小的计算变得简单。

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


public Decimal GetMaximum(Func<IThing, Decimal> 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);

对于C# 2.0和.Net 2.0,你可以执行以下操作:


public delegate Decimal GetProperty<TElement>(TElement element);

public static Decimal Max<TElement>(IEnumerable<TElement> enumeration, 
 GetProperty<TElement> getProperty)
{
 Decimal max = Decimal.MinValue;

 foreach (TElement element in enumeration)
 {
 Decimal propertyValue = getProperty(element);
 max = Math.Max(max, propertyValue);
 }

 return max;
}

下面是使用它的方法:


string[] array = new string[] {"s","sss","ddsddd","333","44432333"};

Max(array, delegate(string e) { return e.Length;});

以下是使用C# 3.0 ,.Net 3.5和Linq的方法,不需要使用上函数:


string[] array = new string[] {"s","sss","ddsddd","333","44432333"};
array.Max( e => e.Length);


public delegate T GetPropertyValueDelegate<T>(IThing t);

public T GetMaximum<T>(GetPropertyValueDelegate<T> getter)
 where T : IComparable
{
 if (this.Count == 0) return default(T);

 T max = getter(this[0]);
 for (int i = 1; i < this.Count; i++)
 {
 T ti = getter(this[i]);
 if (max.CompareTo(ti) < 0) max = ti;
 }
 return max;
}

你可以像这样使用它:


ThingList list;
Decimal maxWeight = list.GetMaximum(delegate(IThing t) { return t.Weight; });

结论:.Net 2.0 (Visual Studio 2005 )没有更好的方法。

如果你不想使用LinqBridge,你仍然可以使用委托,并自己实现Max方法,类似于我所发布的方法:


delegate Decimal PropertyValue(IThing thing);

public class ThingList : IList<IThing> {
 public Decimal Max(PropertyValue prop) {
 Decimal result = Decimal.MinValue;
 foreach (IThing thing in this) {
 result = Math.Max(result, prop(thing));
 }
 return result;
 }
}

使用方法:


ThingList lst;
lst.Max(delegate(IThing thing) { return thing.Age; });

通用的Net 2解决方案如何?


public delegate A AggregateAction<A, B>( A prevResult, B currentElement );

public static Tagg Aggregate<Tcoll, Tagg>( 
 IEnumerable<Tcoll> source, Tagg seed, AggregateAction<Tagg, Tcoll> func )
{
 Tagg result = seed;

 foreach ( Tcoll element in source ) 
 result = func( result, element );

 return result;
}

//this makes max easy
public static int Max( IEnumerable<int> source )
{
 return Aggregate<int,int>( source, 0, 
 delegate( int prev, int curr ) { return curr > prev ? curr : prev; } );
}

//but you could also do sum
public static int Sum( IEnumerable<int> source )
{
 return Aggregate<int,int>( source, 0, 
 delegate( int prev, int curr ) { return curr + prev; } );
}

...