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 .
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 .
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>インターフェイスを実装するオブジェクトに適用すると、最後の要素はインデクサーを介して取得されますが、もちろんそれほど悪くはありません。
*このソースコードは、 ソースコードハイライターで強調表示されました。
- public static TSource LastOrDefault <TSource>( この IEnumerable <TSource>ソース){
- if (source == null ) throw Error.ArgumentNull( "source" );
- IList <TSource> list = source as IList <TSource>;
- if (list!= null ){
- int count = list.Count;
- if (count> 0) return list [count-1];
- }
- その他 {
同様の最適化が、タイプがSingleのメソッドに対して行われます
*このソースコードは、 ソースコードハイライターで強調表示されました。
- public static TSource Single <TSource>( この IEnumerable <TSource>ソース){
- if (source == null ) throw Error.ArgumentNull( "source" );
- IList <TSource> list = source as IList <TSource>;
- if (list!= null ){
- switch (list.Count){
- ケース 0:Error.NoElements()をスローします;
- ケース 1:リスト[0]を返す ;
2. GroupByの仕事
次のようなコードがあるとしましょう:
*このソースコードは、 ソースコードハイライターで強調表示されました。
- クラスキー
- {
- private readonly int _number;
- public static int HashCallsCount;
- public static int EqualsCallsCount;
- 公開鍵( 整数 )
- {
- _number = number;
- }
- public bool Equals(キーその他)
- {
- if (ReferenceEquals( null 、その他))
- {
- falseを 返し ます 。
- }
- if (ReferenceEquals( this 、other))
- {
- trueを 返し ます 。
- }
- return other._number == _number;
- }
- public override bool Equals( オブジェクト obj)
- {
- EqualsCallsCount ++;
- if (ReferenceEquals( null 、obj))
- {
- falseを 返し ます 。
- }
- if (ReferenceEquals( this 、obj))
- {
- trueを 返し ます 。
- }
- if (obj.GetType()!= typeof (キー))
- {
- falseを 返し ます 。
- }
- return Equals((Key)obj);
- }
- パブリック オーバーライド int GetHashCode()
- {
- HashCallsCount ++;
- return _number;
- }
- }
- クラステスト
- {
- public int Number { get ; セット ; }
- パブリック ストリング Name { get ; セット ; }
- 公開鍵キー
- {
- get { return new Key(Number);}
- }
- パブリック オーバーライド 文字列 ToString()
- {
- 戻り 文字列 .Format( "Number:{0}、Name:{1}" 、Number、Name);
- }
- }
- 内部 クラスプログラム
- {
- private static void Main( string [] args)
- {
- var items = Enumerable.Range(1、20).Select(x => new Test {Number = x%3});
- foreach (items.GroupBy(x => x.Key)のvarグループ)
- {
- Console .WriteLine( "グループキー:{0}" 、group.Key);
- foreach (グループ内の varアイテム)
- {
- Console .WriteLine(アイテム);
- }
- }
- Console .WriteLine( "EqualsCallsCount {0}" 、Key.EqualsCallsCount);
- Console .WriteLine( "HashCallsCount {0}" 、Key.HashCallsCount);
- }
- }
EqualsCallsCount変数とHashCallsCount変数にはどのような値が表示されますか? それぞれ17および20。 17-として グループキーになった値は他のキーと比較されません(念のため、この例のグループは正確に3であることを示します)。 20は挑戦です Keyオブジェクトからのハッシュコードを使用して、 Testオブジェクトを配置するグループを決定します。 ハッシュコードが一意の値を与えるのをやめる(たとえば、常に0を返す)場合、Equalsの呼び出し数は38に増えることに注意してください。理由は明らかだと思います。 別の興味深い詳細は、グループのデフォルト配列が7であることです
*このソースコードは、 ソースコードハイライターで強調表示されました。
- Lookup(IEqualityComparer <TKey>比較子){
- if (comparer == null )comparer = EqualityComparer <TKey> .Default;
- this .comparer = comparer;
- グループ化= 新しいグループ化[7];
- }
そして、このステップで配列全体をコピーして、線形的に増加します。
*このソースコードは、 ソースコードハイライターで強調表示されました。
- void Resize(){
- int newSize = チェック (カウント* 2 + 1);
- グループ化[] newGroupings = 新しいグループ化[newSize];
- グループ化g = lastGrouping;
- {
- g = g.next;
- int index = g.hashCode%newSize;
- g.hashNext = newGroupings [インデックス];
- newGroupings [インデックス] = g;
- } while (g!= lastGrouping);
- groupings = newGroupings;
- }
なぜなら GroupByメソッドの容量を指定することはできません。多数のキーでグループ化することは控えた方が良いです。そうしないと、メモリに問題があるかもしれません(もちろん、速度は低下します)。
3.列挙子もオブジェクトです
2つの関数を考えてみましょう。
*このソースコードは、 ソースコードハイライターで強調表示されました。
- private static IEnumerable <Test> SortTest(Func < IEnumerable <Test >>リスト)
- {
- foreach (リストのvarアイテム()。OrderBy(x => x.Number).ThenBy(x => x.Name))
- {
- 返品アイテムを返します。
- }
- }
そして
*このソースコードは、 ソースコードハイライターで強調表示されました。
- private static IEnumerable <Test> SortTest2(Func < IEnumerable <Test >>リスト)
- {
- return list()。OrderBy(x => x.Number).ThenBy(x => x.Name);
- }
list()は、配列などの要素の安定したリストを返すと仮定します。 機能は同等ですか? もちろんそうではありません(最後のリシャーパーには対応するバグがあります )。 最初のケースでは、返された列挙子を調べるときに、 リスト()関数が常に呼び出されます。 したがって、他の値を返し始めると、列挙子の値は異なります。 2番目の場合、 list()関数は1回だけ呼び出され、その結果はOrderedEnumerable <TElement、TKey>クラスのソースフィールドに格納され、将来、返された列挙子をいくら処理しても、その値は同じになります。
囚人
この短い記事が誰かに役立ち、Linqで反対する際のエラーを回避するのに役立つことを願っています。 コメントや追加は大歓迎です。
参照資料
MSDN o LINQ
ご清聴ありがとうございました。