2016-06-21 13 views
1

シーケンスのすべての値が同じかどうかを確認する拡張メソッドをまとめようとしています。その値がintであればその値の平均値を返し、文字列であればその値の連結を返します。例えばC#LINQシーケンスが同じかどうかをチェックして値を返します

は、私はこの

itemsTheSame.AllEqual(item => item.Size)); // should return 1 as an int 
itemsTheSame.AllEqual(item => item.Name)); // should return "Red" as a string 

私のジェネリック医薬品は非常に強力ではないですが、これはIVEが開始するものであるような何かを行うことができるようにしたい、次のオブジェクトとリスト

public class Item 
{ 
    public int Size {get; set;} 
    public string Name {get; set;} 
} 

var itemsTheSame = new List<Item> 
{ 
    new Item { Size = 1, Name = "Red" }, 
    new Item { Size = 1, Name = "Red" }, 
    new Item { Size = 1, Name = "Red" } 
}; 

を取りますoffで返す

public static class Extensions 
{ 
    public static int AllEqual<TSource>(this IEnumerable<TSource> list, Func<TSource, int> selector) 
    { 
     if (!list.Any()) return default(int); 
     var first = list.First(); 
     return list.Skip(1).All(selector == first) ? default(int) : default(int); 
    } 
} 

戻り値の型は汎用のaswである必要がありますエル、そう

public static TReturn AllEqual<TSource, TReturn>(this IEnumerable<TSource> list, Func<TSource, TReturn> selector) 
{ 
    if (!list.Any()) return default(TReturn); 
    var first = list.First(selector); 
    return list.Skip(1).All(selector == first) ? default(TReturn) : default(TReturn); 
} 

私は値そのものを取得することができるように.First()Func<TSource, TReturn> selectorに合格できるようにする必要があるとして、完全に混乱していますが、.FirstFunc<TSource, bool>を必要とする。この(doesentコンパイル)を思い付きました。私はまた、セレクタで指定されたフィールドが等しいかどうかをチェックしたいので、同じセレクタを.Allに渡す必要があります。

オーバーロードされたメソッド、intを返すメソッド、文字列などを返すメソッドのセットを持つ方が良いでしょうか?

フィドルhere

+0

これはちょっと混乱しているようです。例えば"レッド"の平均は何ですか? – stuartd

+1

あなたのメソッドのシグネチャは 'public static TSource AllEqual ...' – Rahul

+0

です。@stuartd TSourceはtypeof(string)と見なされますが、このシナリオでは別個の値の連結を返すことを望みます。 –

答えて

0
  1. IEnumerableを複数回反復しないようにしてください。それは高価すぎるかもしれない、あなたは決して知らない。
  2. 平均的な機能のため、各数値タイプの実装が必要です。
  3. 平均値は、すべて同じ値であるかどうかにかかわらず、実際には同じです。代わりにAverage拡張方式を使用してください。あなたは構文を同じにするためにそれをラップすることができます。 (または、同じ名前ではないので、AllEqualOrAverageのように別の名前を付けることを検討してください)
  4. intの平均は通常doubleです。丸めたい、床を使う、実際にdoubleを返すかどうかを決めます。
  5. 商品から値を取得するにはSelect(selector)が必要です。引数Firstは、Where(predicate)のように述語です。

提案:あなたはコンパイル時に数値型を知っている場合にのみ動作することを

// for doubles (returning average or default if list is empty) 
public static double AllEqual<TSource>(this IEnumerable<TSource> list, Func<TSource, double> selector) 
{ 
    // use IList<T> interface to quickly check the size of the 
    // list if this interface is available. Use ToArray otherwise. 
    var ilist = list as IList<TSource> ?? list.ToArray(); 
    if (ilist.Count == 0) return 0.0; 
    list.Select(selector).Average(); 
} 

// for ints (returning average or default if list is empty) 
public static double AllEqual<TSource>(this IEnumerable<TSource> list, Func<TSource, int> selector) 
{ 
    // use IList<T> interface to quickly check the size of the 
    // list if this interface is available. Use ToArray otherwise. 
    var ilist = list as IList<TSource> ?? list.ToArray(); 
    if (ilist.Count == 0) return 0.0; 
    list.Select(selector).Average(); 
} 

// for everything else (returning unique value or default) 
public static TReturn AllEqual<TSource, TReturn>(this IEnumerable<TSource> list, Func<TSource, TReturn> selector) 
{ 
    // put 2 distinct values into an array 
    var distinctItems.Select(selector).Distinct().Take(2).ToArray(); 
    if (distinct.Length == 1) return distinct[0]; 
    default(TReturn); 
} 

注意。実行時に認識される数値(コンパイル時にオブジェクト)の平均を返す必要がある場合は、実際には難しくなります。

+2

平均を計算する前に「Distinct」を実行すると、その平均値が完全に失われます。 – Servy

+0

私は知っていて、これも言った。それはとにかく固定されています。 –

0

Firstセレクタを受け入れないため、述語を受け入れます。セレクタを最初のアイテムに適用するには、最初のアイテムを(パラメータなしのオーバーロードを使用して)取得し、その結果のセレクタを呼び出す必要があります。

また、不明な型のオブジェクトを比較すると、==演算子を使用できません。これはコンパイル時にバインドされているため、その型の特定の実装とそれがない可能性があります。一致する項目を比較するには、IEqualityComparer<T>を使用する必要があります(指定されていない場合は、デフォルトの比較機能を使用できます)。

また、実装では、ソース列挙型を最大3回繰り返します。それが実際にクエリであり、すでにマテリアライズされていないコレクションであれば、それは簡単に問題になる可能性があります。 1つのLINQクエリでは実行できないので、ここでLINQ操作を使用するのではなく、シーケンスを明示的に反復するほうがはるかに優れています。

関連する問題