オブジェクトに対するLinqの単純な機能または機能についての困難

オブジェクトへのLINQが私たちの生活にしっかりと入り込み、.netアプリケーションのスタック全体に勝利を収めました。 この記事では、LINQを使用する際に対処しなければならなかったいくつかの明白でないことの例を挙げたいと思います。 面白い-猫の下でお願いします。



1.リスト<T>を使用する機能



LINQの実装は、IEnumerable型のオブジェクトが<T>に到着したときと、List <T>が到着したときに異なります。 単純なWhereメソッドでも、異なるイテレータが作成されます。

if (source is Iterator<TSource>) return ((Iterator<TSource>)source).Where(predicate); if (source is TSource[]) return new WhereArrayIterator<TSource>((TSource[])source, predicate); if (source is List <TSource>) return new WhereListIterator<TSource>(( List <TSource>)source, predicate); * This source code was highlighted with Source Code Highlighter .



  1. if (source is Iterator<TSource>) return ((Iterator<TSource>)source).Where(predicate); if (source is TSource[]) return new WhereArrayIterator<TSource>((TSource[])source, predicate); if (source is List <TSource>) return new WhereListIterator<TSource>(( List <TSource>)source, predicate); * This source code was highlighted with Source Code Highlighter .



  2. if (source is Iterator<TSource>) return ((Iterator<TSource>)source).Where(predicate); if (source is TSource[]) return new WhereArrayIterator<TSource>((TSource[])source, predicate); if (source is List <TSource>) return new WhereListIterator<TSource>(( List <TSource>)source, predicate); * This source code was highlighted with Source Code Highlighter .



  3. if (source is Iterator<TSource>) return ((Iterator<TSource>)source).Where(predicate); if (source is TSource[]) return new WhereArrayIterator<TSource>((TSource[])source, predicate); if (source is List <TSource>) return new WhereListIterator<TSource>(( List <TSource>)source, predicate); * This source code was highlighted with Source Code Highlighter .



if (source is Iterator<TSource>) return ((Iterator<TSource>)source).Where(predicate); if (source is TSource[]) return new WhereArrayIterator<TSource>((TSource[])source, predicate); if (source is List <TSource>) return new WhereListIterator<TSource>(( List <TSource>)source, predicate); * This source code was highlighted with Source Code Highlighter .





ところで、配列には独自のイテレータもありますが、イテレータの違いは、インデックス呼び出しを通じて要素を直接受け取ることです。



この機能が私たちにとって興味深いのは何ですか? ええと、たとえば、私は通常、 Last構文を呼び出すことに懐疑的でした。 アイデアによれば、列挙子全体を最後まで通過する必要があります。 ただし、IList <T>インターフェイスを実装するオブジェクトに適用すると、最後の要素はインデクサーを介して取得されますが、もちろんそれほど悪くはありません。





  1. public static TSource LastOrDefault <TSource>( この IEnumerable <TSource>ソース){
  2. if (source == nullthrow Error.ArgumentNull( "source" );
  3. IList <TSource> list = source as IList <TSource>;
  4. if (list!= null ){
  5. int count = list.Count;
  6. if (count> 0) return list [count-1];
  7. }
  8. その他 {
*このソースコードは、 ソースコードハイライターで強調表示されました。


同様の最適化が、タイプがSingleのメソッドに対して行われます





  1. public static TSource Single <TSource>( この IEnumerable <TSource>ソース){
  2. if (source == nullthrow Error.ArgumentNull( "source" );
  3. IList <TSource> list = source as IList <TSource>;
  4. if (list!= null ){
  5. switch (list.Count){
  6. ケース 0:Error.NoElements()をスローします;
  7. ケース 1:リスト[0]を返す ;
*このソースコードは、 ソースコードハイライターで強調表示されました。




2. GroupByの仕事



次のようなコードがあるとしましょう:





  1. クラスキー
  2. {
  3. private readonly int _number;
  4. public static int HashCallsCount;
  5. public static int EqualsCallsCount;
  6. 公開鍵( 整数
  7. {
  8. _number = number;
  9. }
  10. public bool Equals(キーその他)
  11. {
  12. if (ReferenceEquals( null 、その他))
  13. {
  14. falseを 返し ます
  15. }
  16. if (ReferenceEquals( this 、other))
  17. {
  18. trueを 返し ます
  19. }
  20. return other._number == _number;
  21. }
  22. public override bool Equals( オブジェクト obj)
  23. {
  24. EqualsCallsCount ++;
  25. if (ReferenceEquals( null 、obj))
  26. {
  27. falseを 返し ます
  28. }
  29. if (ReferenceEquals( this 、obj))
  30. {
  31. trueを 返し ます
  32. }
  33. if (obj.GetType()!= typeof (キー))
  34. {
  35. falseを 返し ます
  36. }
  37. return Equals((Key)obj);
  38. }
  39. パブリック オーバーライド int GetHashCode()
  40. {
  41. HashCallsCount ++;
  42. return _number;
  43. }
  44. }
  45. クラステスト
  46. {
  47. public int Number { get ; セット ; }
  48. パブリック ストリング Name { get ; セット ; }
  49. 公開鍵キー
  50. {
  51. get { return new Key(Number);}
  52. }
  53. パブリック オーバーライド 文字列 ToString()
  54. {
  55. 戻り 文字列 .Format( "Number:{0}、Name:{1}" 、Number、Name);
  56. }
  57. }
  58. 内部 クラスプログラム
  59. {
  60. private static void Main( string [] args)
  61. {
  62. var items = Enumerable.Range(1、20).Select(x => new Test {Number = x%3});
  63. foreach (items.GroupBy(x => x.Key)のvarグループ)
  64. {
  65. Console .WriteLine( "グループキー:{0}" 、group.Key);
  66. foreach (グループ内の varアイテム)
  67. {
  68. Console .WriteLine(アイテム);
  69. }
  70. }
  71. Console .WriteLine( "EqualsCallsCount {0}" 、Key.EqualsCallsCount);
  72. Console .WriteLine( "HashCallsCount {0}" 、Key.HashCallsCount);
  73. }
  74. }
*このソースコードは、 ソースコードハイライターで強調表示されました。


EqualsCallsCount変数HashCallsCount変数にはどのような値が表示されますか? それぞれ17および20。 17-として グループキーになった値は他のキーと比較されません(念のため、この例のグループは正確に3であることを示します)。 20は挑戦です Keyオブジェクトからのハッシュコードを使用して、 Testオブジェクトを配置するグループを決定します。 ハッシュコードが一意の値を与えるのをやめる(たとえば、常に0を返す)場合、Equalsの呼び出し数は38に増えることに注意してください。理由は明らかだと思います。 別の興味深い詳細は、グループのデフォルト配列が7であることです





  1. Lookup(IEqualityComparer <TKey>比較子){
  2. if (comparer == null )comparer = EqualityComparer <TKey> .Default;
  3. this .comparer = comparer;
  4. グループ化= 新しいグループ化[7];
  5. }
*このソースコードは、 ソースコードハイライターで強調表示されました。


そして、このステップで配列全体をコピーして、線形的に増加します。





  1. void Resize(){
  2. int newSize = チェック (カウント* 2 + 1);
  3. グループ化[] newGroupings = 新しいグループ化[newSize];
  4. グループ化g = lastGrouping;
  5. {
  6. g = g.next;
  7. int index = g.hashCode%newSize;
  8. g.hashNext = newGroupings [インデックス];
  9. newGroupings [インデックス] = g;
  10. } while (g!= lastGrouping);
  11. groupings = newGroupings;
  12. }
*このソースコードは、 ソースコードハイライターで強調表示されました。


なぜなら GroupByメソッドの容量を指定することはできません。多数のキーでグループ化することは控えた方が良いです。そうしないと、メモリに問題があるかもしれません(もちろん、速度は低下します)。



3.列挙子もオブジェクトです



2つの関数を考えてみましょう。





  1. private static IEnumerable <Test> SortTest(Func < IEnumerable <Test >>リスト)
  2. {
  3. foreach (リストのvarアイテム()。OrderBy(x => x.Number).ThenBy(x => x.Name))
  4. {
  5. 返品アイテムを返します。
  6. }
  7. }
*このソースコードは、 ソースコードハイライターで強調表示されました。


そして





  1. private static IEnumerable <Test> SortTest2(Func < IEnumerable <Test >>リスト)
  2. {
  3. return list()。OrderBy(x => x.Number).ThenBy(x => x.Name);
  4. }
*このソースコードは、 ソースコードハイライターで強調表示されました。


list()は、配列などの要素の安定したリストを返すと仮定します。 機能は同等ですか? もちろんそうではありません(最後のリシャーパーには対応するバグがあります )。 最初のケースでは、返された列挙子を調べるときに、 リスト()関数が常に呼び出されます。 したがって、他の値を返し始めると、列挙子の値は異なります。 2番目の場合、 list()関数は1回だけ呼び出され、その結果はOrderedEnumerable <TElement、TKey>クラスソースフィールドに格納され、将来、返された列挙子をいくら処理しても、その値は同じになります。



囚人



この短い記事が誰かに役立ち、Linqで反対する際のエラーを回避するのに役立つことを願っています。 コメントや追加は大歓迎です。



参照資料



MSDN o LINQ



ご清聴ありがとうございました。




All Articles