2008-09-30 68 views
18

我有持有IThing類型的項目一個專門的列表:C#:獲取的所有項目的任意屬性的最大值和最小值在列表

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

有沒有更好的,少臭的方法來實現這一目標?

感謝, 埃裏克

編輯:@馬特: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的想法。不幸的是,我只能「接受」一個答案。

非常感謝。你可以都覺得「接受」,僅altough得到Sklivvz學分;-)

+0

我已經添加了一個工作實現 – Sklivvz 2008-09-30 23:56:40

回答

10

是的,您應該使用委託和匿名方法。

有關示例,請參閱here

基本上你需要實現類似於Find method of Lists的東西。

下面是一個簡單的實現

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

+0

這真是太棒了。 – 2011-07-26 18:47:26

19

如果您在您使用.NET 3.5和LINQ:

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

這將使最小值和最大值相當瑣碎的計算。

8

如果使用.NET 3.5,爲什麼不使用lambdas?

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

這是強類型的,高效的。還有擴展方法已經完成了這一點。

31

(編輯以反映.NET 2.0的答案,LINQBridge在VS2005 ...)

這裏有三種情況 - 雖然OP只有.NET 2.0,面臨着同樣的問題,其他人可能不.. 。

1)使用.NET 3.5和C#3.0:使用LINQ這樣的對象:

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; } 
); 

(I沒有C#2.0編譯器手以測試上述 - 如果它抱怨模糊的轉換,將代理投給函數,十進制>。)

LINQBridge將與VS2005一起使用,但您不會獲得擴展方法,lambda表達式,查詢表達式等顯然,遷移到C#3是一個更好的選擇,但我更喜歡使用LINQBridge來實現它功能我自己。

所有這些建議都涉及如果您需要同時獲取最大值和最小值,請將列表移走兩次。如果你的情況是你從磁盤懶洋洋地加載或者類似的東西,並且你想一次計算多個聚合,你可能想看看MiscUtil中的"Push LINQ"代碼。 (也適用於.NET 2.0。)

2

結論:對於.NET 2.0沒有更好的辦法(與Visual Studio 2005 )。

你似乎誤解了答案(尤其是喬恩的)。您可以使用他的答案中的選項3。如果你不想使用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; }); 
+0

喬恩在作出結論時編輯了他的答案。 – EricSchaefer 2008-09-30 12:15:04

3

對於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,淨3.5和LINQ,沒有上面的功能做到這一點:

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

下面是一個嘗試,使用C#2.0,在Skilwz的想法。

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; }); 
+0

此方法可讓您獲取任何類型實現IComparable的屬性的最大值。因此,您可以獲得最大值,例如DateTime屬性或字符串以及小數。 – 2008-09-30 12:21:49

2

怎麼樣一個廣義的.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; }); 
} 
相關問題