2014-11-04T00:31:51の更新内容

programming/netfx/enumerator/2_iterator/index.wiki.txt

current previous
5,475 5,69
 

        

        
 
#navi(..)
#navi(..)
 

        

        
+
C#では2.0より、VBではVB11(VB2012)より''イテレータ構文''が導入されています。 これは&msdn(netfx,type,System.Collections.Generic.IEnumerable`1){IEnumerator<T>};(または&msdn(netfx,type,System.Collections.IEnumerable){IEnumerable};)の実装をより簡単に記述できるものです。 C#では``yield``キーワード、VBでは``Yield``ステートメントを用いることでイテレータを実装することができます。
+

          
 
-関連するページ
-関連するページ
+
--[[programming/netfx/enumerator/0_enumerable_enumerator]]
 

        

        
 
#adunit
#adunit
 

        

        
 
*イテレータ (反復子)
*イテレータ (反復子)
~
イテレータがどのようなものか具体的な例を見てみます。
C# 2.0よりイテレータ構文が導入されています。 これはIEnumeratorの実装を大幅に簡略化できるもので、VBには用意されていない構文です。 yieldキーワードを用いることで、IEnumerator.MoveNextメソッドの呼び出しとIEnumerator.Currentプロパティの参照に相当する処理を簡単に記述することが出来ます。
-

          
-
以下の例のように、戻り値の型がIEnumerable(IEnumerable<T>)もしくはIEnumerator(IEnumerator<T>)であるメソッド・プロパティでイテレータ構文を記述することができます。 ただし、匿名メソッドでは使用できない、例外処理に制限があるなど、イテレータ構文特有の注意点があります。 詳細は&msdn(netfx,id,dscyy5s0){反復子 (C# プログラミング ガイド)};を参照してください。
 

        

        
~
#tabpage(codelang=cs,container-title=イテレータ)
#code(cs){{
+
#code{{
 
using System;
using System;
-
using System.Collections;
 
using System.Collections.Generic;
using System.Collections.Generic;
 

        

        
~
class Sample {
class Enumerable : IEnumerable
~
  // イテレータメソッド
{
~
  static IEnumerable<string> Enumerate()
  public IEnumerator GetEnumerator()
 
  {
  {
~
    // yield returnでイテレータが列挙する値を返す
    yield return 0;
~
    yield return "foo";
    yield return 1;
~
    yield return "bar";
    yield return 2;
+
    yield return "baz";
+
  }
+

          
+
  static void Main()
+
  {
+
    // 通常のIEnumerable<T>と同様に列挙することができる
+
    foreach (var e in Enumerate()) {
+
      Console.WriteLine(e);
+
    }
 
  }
  }
 
}
}
+
}}
+
#tabpage(codelang=vb)
+
#code{{
+
Imports System
+
Imports System.Collections.Generic
+

          
+
Class Sample
+
  ' イテレータメソッド
+
  Shared Iterator Function Enumerate() As IEnumerable(Of String)
+
    ' Yieldでイテレータが列挙する値を返す
+
    Yield "foo"
+
    Yield "bar"
+
    Yield "baz"
+
  End Function
+

          
+
  Shared Sub Main()
+
    ' 通常のIEnumerable(Of Integer)と同様に列挙することができる
+
    For Each e As String In Enumerate()
+
      Console.WriteLine(e)
+
    Next
+
  End Sub
+
End Class
+
}}
+
#tabpage-end
 

        

        
~
#prompt(実行結果){{
class Sample
~
foo
{
~
bar
  static IEnumerable<string> Enumerate()
+
baz
+
}}
+

          
+
このようにイテレータ構文を使うとIEnumerable・IEnumeratorを実装しなくても列挙ができるようになっています。 C#では``yield return &var{値};``、VBでは``Yield  &var{値};``とすることで列挙される値を返すことができます。 VBでは、イテレータとなるメソッドに``Iterator``修飾子を付ける必要があります。
+

          
+

          
+

          
+
もうひとつ違いと相違点を明らかにするための別の例を挙げます。 イテレータとIEnumerable・IEnumeratorのそれぞれで同じ要素を列挙するクラスを実装すると次のようになります。 (ここでは記述をシンプルにするためにIEnumerable<T>ではなく非ジェネリックなIEnumerableを実装しています)
+

          
+
#column
+
#tabpage(codelang=cs,container-title=イテレータ)
+
#code{{
+
using System;
+
using System.Collections;
+

          
+
class IntIterator : IEnumerable {
+
  public IEnumerator GetEnumerator()
 
  {
  {
+

          
+

          
+

          
+

          
+

          
+

          
+

          
+

          
+

          
+

          
+

          
 
    yield return "foo";
    yield return "foo";
 
    yield return "bar";
    yield return "bar";
 
    yield return "baz";
    yield return "baz";
+

          
+

          
+

          
+

          
 
  }
  }
 

        

        
+

          
+

          
+

          
+

          
+

          
+

          
+

          
+

          
+

          
+

          
+

          
+

          
+

          
+

          
+
}
+

          
+
class Sample {
 
  static void Main()
  static void Main()
 
  {
  {
~
    foreach (var e in new IntIterator()) {
    Console.WriteLine("yield IEnumerator");
+
      Console.WriteLine(e);
+
    }
+
  }
+
}
+

          
+
}}
+
#tabpage(codelang=vb)
+
#code{{
+
Imports System
+
Imports System.Collections
+

          
+
Class IntIterator
+
  Implements IEnumerable
+

          
+
  Public Iterator Function GetEnumerator() As IEnumerator _
+
  Implements IEnumerable.GetEnumerator
+

          
+

          
+

          
+

          
+

          
+

          
+

          
+

          
+

          
+

          
+

          
+

          
+

          
+
    Yield "foo"
+
    Yield "bar"
+
    Yield "baz"
+

          
+

          
+

          
+

          
+
  End Function
+

          
+

          
+

          
+

          
+

          
+

          
+

          
+

          
+

          
+

          
+

          
+

          
+

          
+

          
+

          
+

          
+
End Class
+

          
+
Class Sample
+
  Shared Sub Main()
+
    For Each e As String In New IntIterator()
+
      Console.WriteLine(e)
+
    Next
+
  End Sub
+
End Class
+
}}
+
#tabpage-end
+

          
+
#column
+

          
+
#tabpage(codelang=cs,container-title=IEnumerable・IEnumerator)
+
#code{{
+
using System;
+
using System.Collections;
+

          
+
class IntEnumerable : IEnumerable {
+
  public IEnumerator GetEnumerator()
+
  {
+
    return new IntEnumerator();
+
  }
+
}
 

        

        
~
class IntEnumerator : IEnumerator {
    Enumerable enumerable = new Enumerable();
+
  private int index = -1;
 

        

        
~
  public object Current {
    foreach (int e in enumerable)
+
    get
 
    {
    {
~
      switch (index) {
      Console.WriteLine(e);
+
        case 0: return "foo";
+
        case 1: return "bar";
+
        case 2: return "baz";
+
      }
+

          
+
      throw new InvalidOperationException();
 
    }
    }
+
  }
+

          
+
  public bool MoveNext()
+
  {
+
    index++;
 

        

        
~
    if (index <= 2)
    Console.WriteLine("yield IEnumerable<T>");
+
      return true;
+
    else
+
      return false;
+
  }
 

        

        
~
  public void Reset()
    foreach (string e in Enumerate())
~
  {
    {
+
    throw new NotSupportedException();
+
  }
+
}
+

          
+
class Sample {
+
  static void Main()
+
  {
+
    foreach (var e in new IntEnumerable()) {
 
      Console.WriteLine(e);
      Console.WriteLine(e);
 
    }
    }
 
  }
  }
 
}
}
 
}}
}}
+
#tabpage(codelang=vb)
+
#code{{
+
Imports System
+
Imports System.Collections
+

          
+
Class IntEnumerable
+
  Implements IEnumerable
+

          
+
  Public Function GetEnumerator() As IEnumerator _
+
  Implements IEnumerable.GetEnumerator
+
    Return New IntEnumerator()
+
  End Function
+
End Class
+

          
+
Class IntEnumerator
+
  Implements IEnumerator
+

          
+
  Private index As Integer = -1
+

          
+
  Public ReadOnly Property Current As Object _
+
  Implements IEnumerator.Current
+
    Get
+
      Select Case index
+
        Case 0 : Return "foo"
+
        Case 1 : Return "bar"
+
        Case 2 : Return "baz"
+
      End Select
+

          
+
      Throw New InvalidOperationException()
+
    End Get
+
  End Property
+

          
+
  Public Function MoveNext() As Boolean _
+
  Implements IEnumerator.MoveNext
+
    index += 1
+

          
+
    If index <= 2 Then
+
      Return True
+
    Else
+
      Return False
+
    End If
+
  End Function
+

          
+
  Public Sub Reset() Implements _
+
  IEnumerator.Reset
+
    Throw New NotSupportedException()
+
  End Sub
+
End Class
+

          
+
Class Sample
+
  Shared Sub Main()
+
    For Each e As String In New IntEnumerable()
+
      Console.WriteLine(e)
+
    Next
+
  End Sub
+
End Class
+
}}
+
#tabpage-end
+
#column-end
 

        

        
~
#prompt(実行結果){{
#prompt{{
-
yield IEnumerator
-
0
-
1
-
2
-
yield IEnumerable<T>
 
foo
foo
 
bar
bar
 
baz
baz
 
}}
}}
 

        

        
+
イテレータを使ったコードでは、コンパイル時にCurrentプロパティやMoveNextメソッドなどIEnumerator(またはIEnumerator<T>)に必要なメンバが自動的に実装されます。
+

          
+

          
+
*遅延実行 [#lazy_execution]
+
イテレータの動作は``IEnumerable<T>``を実装した場合と変わりないように見えますが、異なる動作となる部分もあります。
+

          
+
#tabpage(codelang=cs)
+
#code{{
+
using System;
+
using System.Collections.Generic;
+

          
+
class Sample {
+
  // 0から4の値を生成するメソッド
+
  static IEnumerable<int> GenerateNumbers()
+
  {
+
    yield return 0;
+
    yield return 1;
+
    yield return 2;
+
    yield return 3;
+
    yield return 4;
+
  }
+

          
+
  // 列挙される値を倍にして返すメソッド
+
  static IEnumerable<int> Double(IEnumerable<int> list)
+
  {
+
    foreach (var e in list) {
+
      yield return e * 2;
+
    }
+
  }
+

          
+
  static void Main()
+
  {
+
    foreach (var e in Double(GenerateNumbers())) {
+
      Console.WriteLine(e);
+
    }
+
  }
+
}
+
}}
+
#tabpage(codelang=vb)
+
#code{{
+
Imports System
+
Imports System.Collections.Generic
+

          
+
Class Sample
+
  ' 0から4の値を生成するメソッド
+
  Shared Iterator Function GenerateNumbers() As IEnumerable(Of Integer)
+
    Yield 0
+
    Yield 1
+
    Yield 2
+
    Yield 3
+
    Yield 4
+
  End Function
+

          
+
  ' 列挙される値を倍にして返すメソッド
+
  Shared Iterator Function [Double](ByVal list As IEnumerable(Of Integer)) As IEnumerable(Of Integer)
+
    For Each e As Integer In list
+
      Yield e * 2
+
    Next
+
  End Function
+

          
+
  Shared Sub Main()
+
    For Each e As Integer In [Double](GenerateNumbers())
+
      Console.WriteLine(e)
+
    Next
+
  End Sub
+
End Class
+
}}
+
#tabpage-end
+

          
+
#prompt(実行結果){{
+
0
+
2
+
4
+
6
+
8
+
}}
+

          
+
この結果を見ると普通にListなどを列挙する場合と変わらないように見えますが、``yield``は結果が生成されるタイミングが違います。 ``GenerateNumbers``メソッドを次のように変えて``yield``が実行されるタイミングを調べてみると違いが明らかになります。
+

          
+
#tabpage(codelang=cs)
+
#code{{
+
using System;
+
using System.Collections.Generic;
+

          
+
class Sample {
+
  // 0から4の値を生成するメソッド
+
  static IEnumerable<int> GenerateNumbers()
+
  {
+
    Console.WriteLine("yield 0"); yield return 0;
+
    Console.WriteLine("yield 1"); yield return 1;
+
    Console.WriteLine("yield 2"); yield return 2;
+
    Console.WriteLine("yield 3"); yield return 3;
+
    Console.WriteLine("yield 4"); yield return 4;
+
  }
+

          
+
  // 列挙される値を倍にして返すメソッド
+
  static IEnumerable<int> Double(IEnumerable<int> list)
+
  {
+
    foreach (var e in list) {
+
      yield return e * 2;
+
    }
+
  }
+

          
+
  static void Main()
+
  {
+
    foreach (var e in Double(GenerateNumbers())) {
+
      Console.WriteLine(e);
+
    }
+
  }
+
}
+
}}
+
#tabpage(codelang=vb)
+
#code{{
+
Imports System
+
Imports System.Collections.Generic
+

          
+
Class Sample
+
  ' 0から4の値を生成するメソッド
+
  Shared Iterator Function GenerateNumbers() As IEnumerable(Of Integer)
+
    Console.WriteLine("yield 0") : Yield 0
+
    Console.WriteLine("yield 1") : Yield 1
+
    Console.WriteLine("yield 2") : Yield 2
+
    Console.WriteLine("yield 3") : Yield 3
+
    Console.WriteLine("yield 4") : Yield 4
+
  End Function
+

          
+
  ' 列挙される値を倍にして返すメソッド
+
  Shared Iterator Function [Double](ByVal list As IEnumerable(Of Integer)) As IEnumerable(Of Integer)
+
    For Each e As Integer In list
+
      Yield e * 2
+
    Next
+
  End Function
+

          
+
  Shared Sub Main()
+
    For Each e As Integer In [Double](GenerateNumbers())
+
      Console.WriteLine(e)
+
    Next
+
  End Sub
+
End Class
+
}}
+
#tabpage-end
+

          
+
#prompt(実行結果){{
+
yield 0
+
0
+
yield 1
+
2
+
yield 2
+
4
+
yield 3
+
6
+
yield 4
+
8
+
}}
+

          
+
この動作は''遅延実行''と呼ばれるものです。 通常のメソッドとは異なり、イテレータとなるメソッドでは呼び出された時点で``IEnumerable``が返されるものの、イテレータメソッド内の``yield``文は実際に値が列挙されるまでは呼び出されません。
+

          
+

          
+
*イテレータ実装時の注意点
+
イテレータ構文は、戻り値の型が``IEnumerable``(``IEnumerable<T>``)もしくは``IEnumerator``(``IEnumerator<T>``)であるメソッドまたはプロパティで記述することができます。
+

          
+
ただし、匿名メソッドでは使用できない、例外処理に制限があるなど、イテレータ構文特有の注意点があります。 詳細は&msdn(netfx,id,dscyy5s0){反復子 (C# プログラミング ガイド)};を参照してください。
+

          
+

          
+

          
 
#navi(..)
#navi(..)
+

          

programming/netfx/extension_method/index.wiki.txt

current previous
1,636 0,0
+
${smdncms:title,拡張メソッド}
+
${smdncms:keywords,拡張メソッド,ExtensionAttribute}
+
${smdncms:document_versions,codelang=cs,codelang=vb}
+

          
+
一般に、元のクラスに変化を加えること無くメソッドを追加したい場合、派生クラスを作成してそのクラスでメソッドを追加することになります。 拡張メソッド(''extension methods'')を使うと元の型への変更なしにメソッドを''追加''することができます。 追加といっても実際に型に対してメソッドが追加されるのではなく、''型の外部で宣言された静的メソッドを、あたかもインスタンスメソッドのように呼び出す''ことができるようにするのが拡張メソッドです。 拡張メソッドでは、見かけ上は型にメソッドが追加されたように見えます。
+

          
+
-関連するページ
+
--[[programming/netfx/tips/implement_linq]]
+
--[[programming/vb.net/procedure]] (VB)
+

          
+
#adunit
+

          
+
例えば次のような静的メソッドがあったとします。 これを拡張メソッドにすると、あたかもインスタンスメソッドかのように呼び出すことができます。
+

          
+
#column
+

          
+
#tabpage(codelang=cs,container-title=静的メソッド)
+
#code{{
+
using System;
+

          
+
class Sample {
+
  // stringからintに変換する静的メソッド
+
  static int ToInt32(string val)
+
  {
+
    return int.Parse(val);
+
  }
+

          
+
  static void Main()
+
  {
+
    string val = "3";
+

          
+
    // 上記の静的メソッドを呼び出すには
+
    // 次のようにする
+
    int x = ToInt32(val);
+
    int y = ToInt32("42");
+
  }
+
}
+
}}
+
#tabpage(codelang=vb)
+
#code{{
+

          
+
Imports System
+

          
+
Class Sample
+
  ' StringからIntegerに変換する静的メソッド
+
  Shared Function ToInt32(ByVal val As String) As Integer
+
    Return Integer.Parse(val)
+
  End Function
+

          
+
  Shared Sub Main()
+
    Dim val As String = "3"
+

          
+
    ' 上記の静的メソッドを呼び出すには
+
    ' 次のようにする
+
    Dim x As Integer = ToInt32(val)
+
    Dim y As Integer = ToInt32("42")
+
  End Sub
+
End Class
+
}}
+
#tabpage-end
+

          
+
#column
+

          
+
#tabpage(codelang=cs,container-title=拡張メソッド)
+
#code{{
+
using System;
+

          
+
static class Extension {
+
  // stringからintに変換する拡張メソッド
+
  public static int ToInt32(this string val)
+
  {
+
    return int.Parse(val);
+
  }
+
}
+

          
+
class Sample {
+
  static void Main()
+
  {
+
    string val = "3";
+

          
+
    // 拡張メソッドはインスタンスメソッドの
+
    // ように呼び出すことができる
+
    int x = val.ToInt32();
+
    int y = "42".ToInt32();
+
  }
+
}
+
}}
+
#tabpage(codelang=vb)
+
#code{{
+
Imports System
+

          
+
Module Extension
+
  ' StringからIntegerに変換する拡張メソッド
+
  <System.Runtime.CompilerServices.Extension> _
+
  Public Function ToInt32(ByVal val As String) As Integer
+
    Return Integer.Parse(val)
+
  End Function
+
End Module
+

          
+
Class Sample
+
  Shared Sub Main()
+
    Dim val As String = "3"
+

          
+
    ' 拡張メソッドはインスタンスメソッドの
+
    ' ように呼び出すことができる
+
    Dim x As Integer = val.ToInt32()
+
    Dim y As Integer = "42".ToInt32()
+
  End Sub
+
End Class
+
}}
+
#tabpage-end
+

          
+
#column-end
+

          
+
このように、拡張メソッドは静的メソッドをインスタンスメソッドのように呼び出すことができ、拡張メソッドを宣言することにより既存の型に対してメソッドを''追加''することができます。
+

          
+

          
+
*拡張メソッドの使用
+
拡張メソッドを使った代表的な例にLINQがあります。 ``System.Linq``名前空間をインポートすることにより、LINQの拡張メソッドが使えるようになります。 例えば次の例で使用している&msdn(netfx,member,System.Linq.Enumerable.Average){Averageメソッド};は``IEnumerable<T>``インターフェイスに対して追加される拡張メソッドです。
+

          
+
このAverageメソッドは本来``IEnumerable<T>``や&msdn(netfx,type,System.Collections.Generic.List`1){List<T>クラス};には存在しないものですが、次の例のように``List<T>``クラスに追加されたメソッドのように使用することができるようになります。
+

          
+
#tabpage(codelang=cs,container-title=拡張メソッドとLINQ)
+
#code{{
+
using System;
+
using System.Collections.Generic;
+
using System.Linq; // System.Linq名前空間をインポートする
+

          
+
class Sample {
+
  static void Main()
+
  {
+
    var list = new List<int>() {0, 1, 2, 3, 4, 5};
+

          
+
    Console.WriteLine(list.Average()); // 拡張メソッドAverageが使用できるようになる
+
  }
+
}
+
}}
+
#tabpage(codelang=vb)
+
#code{{
+
Imports System
+
Imports System.Collections.Generic
+
Imports System.Linq ' System.Linq名前空間をインポートする
+

          
+
Class Sample
+
  Shared Sub Main()
+
    Dim list As New List(Of Integer) From {0, 1, 2, 3, 4, 5}
+

          
+
    Console.WriteLine(list.Average()) ' 拡張メソッドAverageが使用できるようになる
+
  End Sub
+
End Class
+
}}
+
#tabpage-end
+

          
+
#prompt(実行結果){{
+
2.5
+
}}
+

          
+
拡張メソッドはインスタンスメソッドのように呼び出せる点をのぞけば単なる静的メソッドです。 例えば上記のAverageメソッドも実際には&msdn(netfx,type,System.Linq.Enumerable){Enumerableクラス};で定義されている静的メソッドであるため、次のように静的メソッドとして呼び出すこともできます。
+

          
+
#tabpage(codelang=cs,container-title=LINQメソッドを静的メソッドとして呼び出す)
+
#code{{
+
using System;
+
using System.Collections.Generic;
+

          
+
class Sample {
+
  static void Main()
+
  {
+
    var list = new List<int>() {0, 1, 2, 3, 4, 5};
+

          
+
    Console.WriteLine(System.Linq.Enumerable.Average(list)); // 静的メソッドとして呼び出す
+
  }
+
}
+
}}
+
#tabpage(codelang=vb)
+
#code{{
+
Imports System
+
Imports System.Collections.Generic
+

          
+
Class Sample
+
  Shared Sub Main()
+
    Dim list As New List(Of Integer) From {0, 1, 2, 3, 4, 5}
+

          
+
    Console.WriteLine(System.Linq.Enumerable.Average(list)) ' 静的メソッドとして呼び出す
+
  End Sub
+
End Class
+
}}
+
#tabpage-end
+

          
+

          
+
*拡張メソッドの宣言
+
C#ではメソッドの第一引数に``this``キーワードを前置すると、その引数の型に対する拡張メソッドとして公開することができます。 たとえば``this string &var{val};``と記述すると``string``型に対する拡張メソッドとなります。 一方VBには拡張メソッドを宣言する専用のキーワードは用意されておらず、メソッドに&msdn(netfx,type,System.Runtime.CompilerServices.ExtensionAttribute){Extension属性};(System.Runtime.CompilerServices名前空間)を付与することで拡張メソッドを宣言します。
+

          
+
拡張メソッドは静的クラス(``static class``、VBではモジュール)で宣言する必要があります。 第一引数でメソッドを追加する型を定めるため、拡張メソッドは必然的に引数が一つ以上のメソッドとなります。
+

          
+
#tabpage(codelang=cs,container-title=拡張メソッドの宣言)
+
#code{{
+
using System;
+

          
+
// 拡張メソッドは静的クラスで宣言する必要がある
+
static class Extension {
+
  // 第一引数にthisキーワードを付けると拡張メソッドとなる
+
  // (この場合第一引数がstring型なので、string型に対する拡張メソッドとなる)
+
  public static int ToInt32(this string val)
+
  {
+
    return int.Parse(val);
+
  }
+
}
+

          
+
class Sample {
+
  static void Main()
+
  {
+
    string val = "3";
+

          
+
    int x = val.ToInt32();
+
    int y = "42".ToInt32();
+
  }
+
}
+
}}
+
#tabpage(codelang=vb)
+
#code{{
+
Imports System
+

          
+
' 拡張メソッドはモジュールで宣言する必要がある
+
Module Extension
+
  ' Extension属性を付与すると拡張メソッドとなる
+
  ' (この場合第一引数がString型なので、String型に対する拡張メソッドとなる)
+
  <System.Runtime.CompilerServices.Extension> _
+
  Public Function ToInt32(ByVal val As String) As Integer
+
    Return Integer.Parse(val)
+
  End Function
+
End Module
+

          
+
Class Sample
+
  Shared Sub Main()
+
    Dim val As String = "3"
+

          
+
    Dim x As Integer = val.ToInt32()
+
    Dim y As Integer = "42".ToInt32()
+
  End Sub
+
End Class
+
}}
+
#tabpage-end
+

          
+
``this``キーワードを使った場合は、コンパイル時に自動的にExtension属性が付与されます。 .NET Framework 4以前の場合、Extension属性は``System.Core.dll``で宣言されているため、``System.Core.dll``を参照に追加する必要があります。 (関連:[[programming/netfx/tips/extension_method_without_system_core_dll]])
+

          
+
なお、C#ではVBのようにExtension属性を指定することにより拡張メソッドを実装することはできません。 次のようにコンパイルエラーとなります。
+

          
+
#code(cs){{
+
using System;
+

          
+
static class Extension {
+
  // error CS1112: 'System.Runtime.CompilerServices.ExtensionAttribute' を使用しないでください。 代わりに 'this' キーワードを使用してください。
+
  [System.Runtime.CompilerServices.Extension]
+
  public static int ToInt32(string val)
+
  {
+
    return int.Parse(val);
+
  }
+
}
+
}}
+

          
+
拡張メソッドの場合もほかのメソッドと同様に引数を渡すことができます。 第一引数には拡張メソッドの呼び出しを行ったインスタンス、第二引数以降には拡張メソッドの呼び出しに指定された引数が渡されます。
+

          
+
#tabpage(codelang=cs,container-title=引数を持つ拡張メソッドの宣言)
+
#code{{
+
using System;
+

          
+
static class Extension {
+
  public static string ToHexString(this uint val, bool upperCase)
+
  {
+
    if (upperCase)
+
      // 大文字で16進形式の文字列にする
+
      return val.ToString("X");
+
    else
+
      // 小文字で16進形式の文字列にする
+
      return val.ToString("x");
+
  }
+
}
+

          
+
class Sample {
+
  static void Main()
+
  {
+
    uint val = 3735928495;
+

          
+
    Console.WriteLine(val.ToHexString(true));
+
    Console.WriteLine(val.ToHexString(false));
+
  }
+
}
+
}}
+
#tabpage(codelang=vb)
+
#code{{
+
Imports System
+

          
+
Module Extension
+
  <System.Runtime.CompilerServices.Extension> _
+
  Public Function ToHexString(ByVal val As UInteger, ByVal upperCase As Boolean) As String
+
    If upperCase Then
+
      ' 大文字で16進形式の文字列にする
+
      Return val.ToString("X")
+
    Else
+
      ' 小文字で16進形式の文字列にする
+
      Return val.ToString("x")
+
    End If
+
  End Function
+
End Module
+

          
+
Class Sample
+
  Shared Sub Main()
+
    Dim val As UInteger = 3735928495
+

          
+
    Console.WriteLine(val.ToHexString(True))
+
    Console.WriteLine(val.ToHexString(False))
+
  End Sub
+
End Class
+
}}
+
#tabpage-end
+

          
+
#prompt(実行結果){{
+
DEADBEAF
+
deadbeaf
+
}}
+

          
+
拡張メソッドを宣言するクラスには任意の名前を使用することができます。 必ずしも拡張メソッドを追加する型と関連する名前となっている必要はありませんが、拡張メソッドを含むクラスであることをわかりやすくするために``XxxExtension(s)``というクラス名(``string``型なら``StringExtension``など)にすることが多いようです。
+

          
+
拡張メソッドはクラス・構造体・インターフェイスのほか、列挙体に対しても追加することができます。 これにより、列挙体にメソッドを持たせるようにすることができます。
+

          
+
#tabpage(codelang=cs,container-title=列挙体に拡張メソッドを追加する)
+
#code{{
+
using System;
+
using System.IO;
+

          
+
static class Extension {
+
  public static bool IsReadOnly(this FileAttributes attr)
+
  {
+
    return attr.HasFlag(FileAttributes.ReadOnly);
+
  }
+
}
+

          
+
class Sample {
+
  static void Main()
+
  {
+
    var attr1 = FileAttributes.System | FileAttributes.ReadOnly;
+
    var attr2 = FileAttributes.Normal;
+

          
+
    Console.WriteLine(attr1.IsReadOnly());
+
    Console.WriteLine(attr2.IsReadOnly());
+
    Console.WriteLine(FileAttributes.ReadOnly.IsReadOnly());
+
  }
+
}
+
}}
+
#tabpage(codelang=vb)
+
#code{{
+
Imports System
+
Imports System.IO
+

          
+
Module Extension
+
  <System.Runtime.CompilerServices.Extension> _
+
  Public Function IsReadOnly(ByVal attr As FileAttributes) As Boolean
+
    Return attr.HasFlag(FileAttributes.ReadOnly)
+
  End Function
+
End Module
+

          
+
Class Sample
+
  Shared Sub Main()
+
    Dim attr1 As FileAttributes = FileAttributes.System Or FileAttributes.ReadOnly
+
    Dim attr2 As FileAttributes = FileAttributes.Normal
+

          
+
    Console.WriteLine(attr1.IsReadOnly())
+
    Console.WriteLine(attr2.IsReadOnly())
+
    Console.WriteLine(FileAttributes.ReadOnly.IsReadOnly())
+
  End Sub
+
End Class
+
}}
+
#tabpage-end
+

          
+
#prompt(実行結果){{
+
True
+
False
+
True
+
}}
+

          
+

          
+
*拡張メソッドを使用・宣言する際の注意点
+
異なる名前空間で宣言されている拡張メソッドを使用する場合は、その名前空間をインポートしておく必要があります。 クラス・構造体など他の型と同様、インポートされていない名前空間で宣言されている拡張メソッドを参照しようとしても名前を解決できずコンパイルエラーとなります。
+

          
+
#tabpage(codelang=cs,container-title=名前空間のインポート)
+
#code{{
+
using System;
+

          
+
using MyNamespace; // 拡張メソッドを使用するには宣言されている名前空間をインポートする
+

          
+
namespace MyApplication {
+
  class Sample {
+
    static void Main()
+
    {
+
      string val = "3";
+

          
+
      int x = val.ToInt32();
+
      int y = "42".ToInt32();
+
    }
+
  }
+
}
+

          
+
namespace MyNamespace {
+
  static class Extension {
+
    public static int ToInt32(this string val)
+
    {
+
      return int.Parse(val);
+
    }
+
  }
+
}
+
}}
+
#tabpage(codelang=vb)
+
#code{{
+
Imports System
+

          
+
Imports MyNamespace ' 拡張メソッドを使用するには宣言されている名前空間をインポートする
+

          
+
Namespace MyApplication
+
  Class Sample
+
    Shared Sub Main()
+
      Dim val As String = "3"
+

          
+
      Dim x As Integer = val.ToInt32()
+
      Dim y As Integer = "42".ToInt32()
+
    End Sub
+
  End Class
+
End Namespace
+

          
+
Namespace MyNamespace
+
  Module Extension
+
    <System.Runtime.CompilerServices.Extension> _
+
    Public Function ToInt32(ByVal val As String) As Integer
+
      Return Integer.Parse(val)
+
    End Function
+
  End Module
+
End Namespace
+
}}
+
#tabpage-end
+

          
+
拡張メソッドは``null``/``Nothing``が代入されている変数からも呼び出すことができます。 見かけ上はヌル参照となるように見えますが、実際のところこれは引数に``null``/``Nothing``を指定して静的メソッドを呼び出すことと変わらないため、実行時エラーとはなりません。
+

          
+
#tabpage(codelang=cs,container-title=nullが代入されている変数からの拡張メソッド呼び出し)
+
#code{{
+
using System;
+

          
+
static class Extension {
+
  public static string Quote(this string val)
+
  {
+
    return string.Concat("\"", val, "\"");
+
  }
+
}
+

          
+
class Sample {
+
  static void Main()
+
  {
+
    string x = "Hello, world!";
+
    string y = null;
+

          
+
    Console.WriteLine(x.Quote());
+
    Console.WriteLine(y.Quote());
+
    Console.WriteLine(((string)null).Quote());
+

          
+
    // 上記の拡張メソッド呼び出しは、以下の静的メソッド呼び出しと同じなので
+
    // いずれもヌル参照とはならない
+
    //Console.WriteLine(Extension.Quote(x));
+
    //Console.WriteLine(Extension.Quote(y));
+
    //Console.WriteLine(Extension.Quote((string)null));
+
  }
+
}
+
}}
+
#tabpage(codelang=vb)
+
#code{{
+
Imports System
+

          
+
Module Extension
+
  <System.Runtime.CompilerServices.Extension> _
+
  Public Function Quote(ByVal val As String) As String
+
    Return String.Concat("""", val, """")
+
  End Function
+
End Module
+

          
+
Class Sample
+
  Shared Sub Main()
+
    Dim x As String = "Hello, world!"
+
    Dim y As String = Nothing
+

          
+
    Console.WriteLine(x.Quote())
+
    Console.WriteLine(y.Quote())
+
    Console.WriteLine(CType(Nothing, String).Quote())
+

          
+
    ' 上記の拡張メソッド呼び出しは、以下の静的メソッド呼び出しと同じなので
+
    ' いずれもヌル参照とはならない
+
    'Console.WriteLine(Extension.Quote(x))
+
    'Console.WriteLine(Extension.Quote(y))
+
    'Console.WriteLine(Extension.Quote(CType(Nothing, String)))
+
  End Sub
+
End Class
+
}}
+
#tabpage-end
+

          
+
#prompt(実行結果){{
+
"Hello, world!"
+
""
+
""
+
}}
+

          
+
拡張メソッドは見かけ上はインスタンスメソッドですが、あくまで''別のクラスで宣言された静的メソッド''であるため、拡張メソッド内で``private``や``protected``など外部に公開されていないインスタンスメンバを参照したり呼び出したりすることはできません。 通常のメソッドと同様、拡張メソッド内でアクセスできるのは外部に公開されているメンバのみです。
+

          
+
#tabpage(codelang=cs,container-title=拡張メソッドとインスタンスメンバの参照)
+
#code{{
+
using System;
+

          
+
class Account {
+
  public string DisplayName;
+
  private int _id;
+

          
+
  public Account(int id, string displayName)
+
  {
+
    this._id = id;
+
    this.DisplayName = displayName;
+
  }
+
}
+

          
+
static class Extension {
+
  public static int GetID(this Account account)
+
  {
+
    // 拡張メソッド内からクラスの非公開メンバにアクセスすることはできない
+
    return account._id;
+
    // error CS0122: `Account._id' はアクセスできない保護レベルになっています。
+
  }
+
}
+

          
+
class Sample {
+
  static void Main()
+
  {
+
    var a = new Account(0, "Alice");
+

          
+
    Console.WriteLine(a.GetID());
+
  }
+
}
+
}}
+
#tabpage(codelang=vb)
+
#code{{
+
Imports System
+

          
+
Class Account
+
  Public DisplayName As String
+
  Private _id As Integer
+

          
+
  Public Sub New(ByVal id As Integer, ByVal displayName As String)
+
    Me._id = id
+
    Me.DisplayName = DisplayName
+
  End Sub
+
End Class
+

          
+
Module Extension
+
  <System.Runtime.CompilerServices.Extension> _
+
  Public Function GetID(ByVal account As Account) As Integer
+
    ' 拡張メソッド内からクラスの非公開メンバにアクセスすることはできない
+
    Return account._id
+
    ' error BC30390: 'Account._id' は 'Private' であるため、このコンテキストではアクセスできません。
+
  End Function
+
End Module
+

          
+
Class Sample
+
  Shared Sub Main()
+
    Dim a As New Account(0, "Alice")
+

          
+
    Console.WriteLine(a.GetID())
+
  End Sub
+
End Class
+
}}
+
#tabpage-end
+

          
+
拡張メソッドと同名で同じシグネチャ(引数リスト)のメソッドがすでに型に存在している場合は、型に存在するメソッドの呼び出しが優先されます。 同名のメソッドが存在する場合でも、拡張メソッドとしてではなく静的メソッドとして呼び出すことはできます。
+

          
+
#tabpage(codelang=cs,container-title=拡張メソッドと同名のメソッドが存在する場合)
+
#code{{
+
using System;
+
using System.Collections.Generic;
+

          
+
static class Extension {
+
  // Listクラスに存在するメソッドと同名の拡張メソッド
+
  public static void Add<T>(this List<T> list, T item)
+
  {
+
    Console.WriteLine("extension method!");
+
  }
+
}
+

          
+
class Sample {
+
  static void Main()
+
  {
+
    var list = new List<int>();
+

          
+
    list.Add(0); // 拡張メソッドではなくListクラスのAddメソッドが呼び出される
+

          
+
    Console.WriteLine("added!");
+

          
+
    Extension.Add(list, 0); // 静的メソッドとして呼び出すことはできる
+
  }
+
}
+
}}
+
#tabpage(codelang=vb)
+
#code{{
+
Imports System
+
Imports System.Collections.Generic
+

          
+
Module Extension
+
  '  Listクラスに存在するメソッドと同名の拡張メソッド
+
  <System.Runtime.CompilerServices.Extension> _
+
  Public Sub Add(Of T)(ByVal list As List(Of T), ByVal item As T)
+
    Console.WriteLine("extension method!")
+
  End Sub
+
End Module
+

          
+
Class Sample
+
  Shared Sub Main()
+
    Dim list As New List(Of Integer)()
+

          
+
    list.Add(0) ' 拡張メソッドではなくListクラスのAddメソッドが呼び出される
+

          
+
    Console.WriteLine("added!")
+

          
+
    Extension.Add(list, 0) ' 静的メソッドとして呼び出すことはできる
+
  End Sub
+
End Class
+
}}
+
#tabpage-end
+

          
+
#prompt(実行結果){{
+
added!
+
extension method!
+
}}
+

          
+

          
+

          

programming/netfx/tips/implement_linq/index.wiki.txt

current previous
1,1964 0,0
+
${smdncms:title,LINQ:実装して理解する}
+
${smdncms:keywords,System.Linq,Enumerable,LINQ,拡張メソッド}
+

          
+
LINQと同じ動作をするメソッドを実装することにより、具体的なコードを使って理解する。 またコードレベルでの動作を知ることにより適切な形でLINQを使うことができるようにする。
+

          
+
-関連するページ
+
--[[programming/netfx/extension_method]]
+
--[[programming/netfx/enumerator]]
+
---[[programming/netfx/enumerator/0_enumerable_enumerator]]
+
---[[programming/netfx/enumerator/2_iterator]]
+
--[[programming/netfx/collections]]
+
---[[programming/netfx/collections/2_generic_1_list]]
+
--[[programming/netfx/sorting/0_basictypes#Enumerable.OrderBy]]
+
--[[programming/netfx/tips/shuffle_array_collection]]
+
--[[programming/netfx/delegate/0_abstract]]
+

          
+
#adunit
+

          
+
以下のサンプルでは簡単化のため引数チェックなどの処理は省略している。 また、ここではLINQのメソッド構文のみを扱い、クエリ構文は扱わない。
+

          
+
*導入 [#introduction]
+
**Count [#Count]
+
LINQのメソッドがどのような実装になっているかを理解するために、&msdn(netfx,member,System.Linq.Enumerable.Count){Countメソッド};と同じ動作をするメソッドを実装することを考える。 まず、LINQのCountメソッドを使って要素数を取得する場合は次のようになる。
+

          
+
#tabpage(codelang=cs,container-title=Countメソッド)
+
#code{{
+
using System;
+
using System.Collections.Generic;
+
using System.Linq;
+

          
+
class Sample {
+
  static void Main()
+
  {
+
    IEnumerable<int> source = new List<int>() {0, 1, 2, 3, 4};
+

          
+
    Console.WriteLine(source.Count());
+
  }
+
}
+
}}
+
#tabpage(codelang=vb)
+
#code{{
+
Option Strict On
+
Option Infer On
+

          
+
Imports System
+
Imports System.Collections.Generic
+
Imports System.Linq
+

          
+
Class Sample
+
  Shared Sub Main()
+
    Dim source As IEnumerable(Of Integer) = New List(Of Integer) From {0, 1, 2, 3, 4}
+

          
+
    Console.WriteLine(source.Count())
+
  End Sub
+
End Class
+
}}
+
#tabpage-end
+

          
+
これと同等の処理を行う方法のひとつとして次のようにする方法が考えられる。 つまり、単に``foreach``により''ソースシーケンス''(リスト・配列・コレクションや、LINQメソッドの戻り値として返される``IEnumerable<T>``など)を列挙し、列挙できた要素を数え上げる。 (``ICollection<T>``ならばCountプロパティを参照することもできるが、ここでは``IEnumerable<T>``に限定して考える)
+

          
+
#tabpage(codelang=cs,container-title=IEnumerable<T>の要素数を数える方法)
+
#code{{
+
using System;
+
using System.Collections.Generic;
+

          
+
class Sample {
+
  static void Main()
+
  {
+
    IEnumerable<int> source = new List<int>() {0, 1, 2, 3, 4};
+

          
+
    int count = 0;
+

          
+
    foreach (var e in source) {
+
      count++; // 列挙した回数=列挙できた要素数を計上する
+
    }
+

          
+
    Console.WriteLine(count);
+
  }
+
}
+
}}
+
#tabpage(codelang=vb)
+
#code{{
+
Option Strict On
+
Option Infer On
+

          
+
Imports System
+
Imports System.Collections.Generic
+

          
+
Class Sample
+
  Shared Sub Main()
+
    Dim source As IEnumerable(Of Integer) = New List(Of Integer) From {0, 1, 2, 3, 4}
+

          
+
    Dim count As Integer = 0
+

          
+
    For Each e In source
+
      count += 1 ' 列挙した回数=列挙できた要素数を計上する
+
    Next
+

          
+
    Console.WriteLine(count)
+
  End Sub
+
End Class
+
}}
+
#tabpage-end
+

          
+
次に、このアルゴリズムを汎用的に使用できるようメソッド化すると次のようになる。
+

          
+
#tabpage(codelang=cs,container-title=メソッド化した計上アルゴリズム)
+
#code{{
+
using System;
+
using System.Collections.Generic;
+

          
+
class Sample {
+
  static int Count(IEnumerable<int> source)
+
  {
+
    int count = 0;
+

          
+
    foreach (var e in source) {
+
      count++;
+
    }
+

          
+
    return count;
+
  }
+

          
+
  static void Main()
+
  {
+
    IEnumerable<int> source = new List<int>() {0, 1, 2, 3, 4};
+

          
+
    Console.WriteLine(Count(source));
+
  }
+
}
+
}}
+
#tabpage(codelang=vb)
+
#code{{
+
Option Strict On
+
Option Infer On
+

          
+
Imports System
+
Imports System.Collections.Generic
+

          
+
Class Sample
+
  Shared Function Count(ByVal source As IEnumerable(Of Integer)) As Integer
+
    Dim cnt As Integer = 0
+

          
+
    For Each e In source
+
      cnt += 1 ' 列挙した回数=列挙できた要素数を計上する
+
    Next
+

          
+
    Return cnt
+
  End Function
+

          
+
  Shared Sub Main()
+
    Dim source As IEnumerable(Of Integer) = New List(Of Integer) From {0, 1, 2, 3, 4}
+

          
+
    Console.WriteLine(Count(source))
+
  End Sub
+
End Class
+
}}
+
#tabpage-end
+

          
+
これでは対象が``int``型のソースシーケンス(``IEnumerable<int>``)に限定されてしまうので、さらに汎用的に使用できるよう型指定をパラメータ化してジェネリックメソッドにし、任意の型のソースシーケンス(``IEnumerable<TSource>``)に対して使用できるようにする。
+

          
+
#tabpage(codelang=cs,container-title=ジェネリックメソッド化した計上アルゴリズム)
+
#code{{
+
using System;
+
using System.Collections.Generic;
+

          
+
class Sample {
+
  static int Count<TSource>(IEnumerable<TSource> source)
+
  {
+
    int count = 0;
+

          
+
    foreach (var e in source) {
+
      count++;
+
    }
+

          
+
    return count;
+
  }
+

          
+
  static void Main()
+
  {
+
    IEnumerable<int> source = new List<int>() {0, 1, 2, 3, 4};
+

          
+
    Console.WriteLine(Count(source));
+
  }
+
}
+
}}
+
#tabpage(codelang=vb)
+
#code{{
+
Option Strict On
+
Option Infer On
+

          
+
Imports System
+
Imports System.Collections.Generic
+

          
+
Class Sample
+
  Shared Function Count(Of TSource)(ByVal source As IEnumerable(Of TSource)) As Integer
+
    Dim cnt As Integer = 0
+

          
+
    For Each e In source
+
      cnt += 1 ' 列挙した回数=列挙できた要素数を計上する
+
    Next
+

          
+
    Return cnt
+
  End Function
+

          
+
  Shared Sub Main()
+
    Dim source As IEnumerable(Of Integer) = New List(Of Integer) From {0, 1, 2, 3, 4}
+

          
+
    Console.WriteLine(Count(source))
+
  End Sub
+
End Class
+
}}
+
#tabpage-end
+

          
+
さらに、LINQのCountメソッドと同様インスタンスメソッドのように呼び出せるようにするため、このメソッドを''拡張メソッド''にする。 (詳細:[[programming/netfx/extension_method]]) 拡張メソッドは静的クラス(``static class``, VBではモジュール)で実装する必要があるので、先のメソッドをクラス化する。 (ここでは``Enumerable``というクラス名にする)
+

          
+
#tabpage(codelang=cs,container-title=拡張メソッド化した計上アルゴリズム)
+
#code{{
+
using System;
+
using System.Collections.Generic;
+

          
+
public static class Enumerable {
+
  public static int Count<TSource>(this IEnumerable<TSource> source)
+
  {
+
    int count = 0;
+

          
+
    foreach (var e in source) {
+
      count++;
+
    }
+

          
+
    return count;
+
  }
+
}
+

          
+
class Sample {
+
  static void Main()
+
  {
+
    IEnumerable<int> source = new List<int>() {0, 1, 2, 3, 4};
+

          
+
    Console.WriteLine(source.Count()); // インスタンスメソッドのように呼び出せる
+
  }
+
}
+
}}
+
#tabpage(codelang=vb)
+
#code{{
+
Option Strict On
+
Option Infer On
+

          
+
Imports System
+
Imports System.Collections.Generic
+

          
+
Public Module Enumerable
+
  <System.Runtime.CompilerServices.Extension()> _
+
  Public Function Count(Of TSource)(ByVal source As IEnumerable(Of TSource)) As Integer
+
    Dim cnt As Integer = 0
+

          
+
    For Each e In source
+
      cnt += 1
+
    Next
+

          
+
    Return cnt
+
  End Function
+
End Module
+

          
+
Class Sample
+
  Shared Sub Main()
+
    Dim source As IEnumerable(Of Integer) = New List(Of Integer) From {0, 1, 2, 3, 4}
+

          
+
    Console.WriteLine(source.Count()) ' インスタンスメソッドのように呼び出せる
+
  End Sub
+
End Class
+
}}
+
#tabpage-end
+

          
+
これでLINQのCountメソッドと同等のメソッドを作ることができた。 ここまでの要点は、
+
+LINQのメソッドは''任意の型のソースシーケンス''(``IEnumerable<TSource>``)を対象としたアルゴリズム
+
+アルゴリズムを''拡張メソッド''として実装することでインスタンスメソッドのように呼び出せる
+

          
+

          
+

          
+
**First, Last [#First_Last]
+
&msdn(netfx,member,System.Linq.Enumerable.First){Firstメソッド};も同様にして実装することができる。 Firstメソッドはソースシーケンスから最初の要素を取得して返す。 ソースシーケンスが空の場合は最初の要素を定義できないため、例外をスローする。
+

          
+
#tabpage(codelang=cs,container-title=Firstメソッドの実装)
+
#code{{
+
using System;
+
using System.Collections.Generic;
+

          
+
public static class Enumerable {
+
  public static TSource First<TSource>(this IEnumerable<TSource> source)
+
  {
+
    foreach (var e in source) {
+
      return e; // 最初に列挙された要素を返す
+
    }
+

          
+
    // 列挙する要素がない=シーケンスが空の場合は例外をスローする
+
    throw new InvalidOperationException("ソースシーケンスが空です");
+
  }
+
}
+

          
+
class Sample {
+
  static void Main()
+
  {
+
    IEnumerable<int> source = new List<int>() {0, 1, 2, 3, 4};
+

          
+
    Console.WriteLine(source.First());
+
  }
+
}
+
}}
+
#tabpage(codelang=vb)
+
#code{{
+
Option Strict On
+
Option Infer On
+

          
+
Imports System
+
Imports System.Collections.Generic
+

          
+
Public Module Enumerable
+
  <System.Runtime.CompilerServices.Extension()> _
+
  Public Function First(Of TSource)(ByVal source As IEnumerable(Of TSource)) As TSource
+
    For Each e In source
+
      Return e ' 最初に列挙された要素を返す
+
    Next
+

          
+
    ' 列挙する要素がない=シーケンスが空の場合は例外をスローする
+
    Throw New InvalidOperationException("ソースシーケンスが空です")
+
  End Function
+
End Module
+

          
+
Class Sample
+
  Shared Sub Main()
+
    Dim source As IEnumerable(Of Integer) = New List(Of Integer) From {0, 1, 2, 3, 4}
+

          
+
    Console.WriteLine(source.First())
+
  End Sub
+
End Class
+
}}
+
#tabpage-end
+

          
+
#prompt(実行結果){{
+
0
+
}}
+

          
+
一方&msdn(netfx,member,System.Linq.Enumerable.FirstOrDefault){FirstOrDefaultメソッド};では、ソースシーケンスが空の場合にはデフォルトの値を返すとされている。 これを実装すると次のようになる。
+

          
+
#tabpage(codelang=cs,container-title=FirstOrDefaultメソッドの実装)
+
#code{{
+
using System;
+
using System.Collections.Generic;
+

          
+
public static class Enumerable {
+
  public static TSource FirstOrDefault<TSource>(this IEnumerable<TSource> source)
+
  {
+
    foreach (var e in source) {
+
      return e; // 最初に列挙された要素を返す
+
    }
+

          
+
    // 列挙する要素がない=シーケンスが空の場合はデフォルトの値を返す
+
    return default(TSource);
+
  }
+
}
+

          
+
class Sample {
+
  static void Main()
+
  {
+
    IEnumerable<int> source = new List<int>() {2, 3, 4};
+

          
+
    Console.WriteLine(source.FirstOrDefault());
+

          
+
    IEnumerable<int> empty = new List<int>();
+

          
+
    Console.WriteLine(empty.FirstOrDefault());
+
  }
+
}
+
}}
+
#tabpage(codelang=vb)
+
#code{{
+
Option Strict On
+
Option Infer On
+

          
+
Imports System
+
Imports System.Collections.Generic
+

          
+
Public Module Enumerable
+
  <System.Runtime.CompilerServices.Extension()> _
+
  Public Function FirstOrDefault(Of TSource)(ByVal source As IEnumerable(Of TSource)) As TSource
+
    For Each e In source
+
      Return e ' 最初に列挙された要素を返す
+
    Next
+

          
+
    ' 列挙する要素がない=シーケンスが空の場合はデフォルトの値を返す
+
    Return Nothing
+
  End Function
+
End Module
+

          
+
Class Sample
+
  Shared Sub Main()
+
    Dim source As IEnumerable(Of Integer) = New List(Of Integer) From {2, 3, 4}
+

          
+
    Console.WriteLine(source.FirstOrDefault())
+

          
+
    Dim empty As IEnumerable(Of Integer) = New List(Of Integer)()
+

          
+
    Console.WriteLine(empty.FirstOrDefault())
+
  End Sub
+
End Class
+
}}
+
#tabpage-end
+

          
+
#prompt(実行結果){{
+
2
+
0
+
}}
+

          
+
Firstメソッドでは例外をスローしていた箇所を、FirstOrDefaultメソッドではデフォルト値を返すように置き換えている。 ジェネリックメソッドで型に応じたデフォルト値を返すには``default(&var{型};)``(VBでは``Nothing``)を使用する。 ([[programming/netfx/basic_types/0_characteristics#DefaultValue]])
+

          
+
&msdn(netfx,member,System.Linq.Enumerable.Last){Lastメソッド};および&msdn(netfx,member,System.Linq.Enumerable.LastOrDefault){LastOrDefaultメソッド};も、``default``(または``Nothing``)を使うことで同様に実装できる。
+

          
+
#tabpage(codelang=cs,container-title=Lastメソッド・LastOrDefaultメソッドの実装)
+
#code{{
+
using System;
+
using System.Collections.Generic;
+

          
+
public static class Enumerable {
+
  public static TSource Last<TSource>(this IEnumerable<TSource> source)
+
  {
+
    TSource last = default(TSource);
+
    bool isEmpty = true;
+

          
+
    foreach (var e in source) {
+
      last = e; // 最後に列挙された要素を保持する
+
      isEmpty = false; // ひとつでも列挙されたらfalse(=空ではない)にする
+
    }
+

          
+
    if (isEmpty)
+
      // 要素が1つも列挙されなかった場合
+
      throw new InvalidOperationException("ソースシーケンスが空です");
+
    else
+
      // 列挙された場合は最後に列挙された要素を返す
+
      return last;
+
  }
+

          
+
  public static TSource LastOrDefault<TSource>(this IEnumerable<TSource> source)
+
  {
+
    TSource last = default(TSource);
+

          
+
    foreach (var e in source) {
+
      last = e; // 最後に列挙された要素を保持する
+
    }
+

          
+
    // 最後に列挙された要素、またはデフォルトの値を返す
+
    return last;
+
  }
+
}
+

          
+
class Sample {
+
  static void Main()
+
  {
+
    IEnumerable<int> source = new List<int>() {0, 1, 2, 3, 4};
+

          
+
    Console.WriteLine(source.Last());
+

          
+
    IEnumerable<int> empty = new List<int>();
+

          
+
    Console.WriteLine(empty.LastOrDefault());
+
  }
+
}
+
}}
+
#tabpage(codelang=vb)
+
#code{{
+
Option Strict On
+
Option Infer On
+

          
+
Imports System
+
Imports System.Collections.Generic
+

          
+
Public Module Enumerable
+
  <System.Runtime.CompilerServices.Extension()> _
+
  Public Function Last(Of TSource)(ByVal source As IEnumerable(Of TSource)) As TSource
+
    Dim lastItem As TSource = Nothing
+
    Dim isEmpty As Boolean = True
+

          
+
    For Each e In source
+
      lastItem = e ' 最後に列挙された要素を保持する
+
      isEmpty = False ' ひとつでも列挙されたらfalse(=空ではない)にする
+
    Next
+

          
+
    If isEmpty Then
+
      ' 要素が1つも列挙されなかった場合
+
      Throw New InvalidOperationException("ソースシーケンスが空です")
+
    Else
+
      ' 列挙された場合は最後に列挙された要素を返す
+
      Return lastItem
+
    End If
+
  End Function
+

          
+
  <System.Runtime.CompilerServices.Extension()> _
+
  Public Function LastOrDefault(Of TSource)(ByVal source As IEnumerable(Of TSource)) As TSource
+
    Dim lastItem As TSource = Nothing
+

          
+
    For Each e In source
+
      lastItem = e ' 最後に列挙された要素を保持する
+
    Next
+

          
+
    ' 最後に列挙された要素、またはデフォルトの値を返す
+
    Return lastItem
+
  End Function
+
End Module
+

          
+
Class Sample
+
  Shared Sub Main()
+
    Dim source As IEnumerable(Of Integer) = New List(Of Integer) From {0, 1, 2, 3, 4}
+

          
+
    Console.WriteLine(source.Last())
+

          
+
    Dim empty As IEnumerable(Of Integer) = New List(Of Integer)()
+

          
+
    Console.WriteLine(empty.LastOrDefault())
+
  End Sub
+
End Class
+
}}
+
#tabpage-end
+

          
+

          
+
**Skip, Take [#Skip_Take]
+
&msdn(netfx,member,System.Linq.Enumerable.Skip){Skipメソッド};はソースシーケンスから最初の&var{n};個の要素を除いた部分(スキップした部分)を取り出す。 逆に&msdn(netfx,member,System.Linq.Enumerable.Take){Takeメソッド};はソースシーケンスから最初の&var{n};個の要素のみを取り出す。
+

          
+
#tabpage(codelang=cs,container-title=Skipメソッド・Takeメソッド)
+
#code{{
+
using System;
+
using System.Collections.Generic;
+
using System.Linq;
+

          
+
class Sample {
+
  static void Main()
+
  {
+
    var source = new List<int>() {0, 1, 2, 3, 4};
+

          
+
    // 先頭から3つの要素を除いた残りの要素を取り出して表示する
+
    Console.WriteLine(string.Join(", ", source.Skip(3)));
+

          
+
    // 先頭から3つの要素を取り出して表示する
+
    Console.WriteLine(string.Join(", ", source.Take(3)));
+
  }
+
}
+
}}
+
#tabpage(codelang=vb)
+
#code{{
+
Option Strict On
+
Option Infer On
+

          
+
Imports System
+
Imports System.Collections.Generic
+
Imports System.Linq
+

          
+
Class Sample
+
  Shared Sub Main()
+
    Dim source As IEnumerable(Of Integer) = New List(Of Integer) From {0, 1, 2, 3, 4}
+

          
+
    ' 先頭から3つの要素を除いた残りの要素を取り出して表示する
+
    Console.WriteLine(String.Join(", ", source.Skip(3)))
+

          
+
    ' 先頭から3つの要素を取り出して表示する
+
    Console.WriteLine(String.Join(", ", source.Take(3)))
+
  End Sub
+
End Class
+
}}
+
#tabpage-end
+

          
+
#prompt(実行結果){{
+
3, 4
+
0, 1, 2
+
}}
+

          
+
これを実装すると次のようになる。
+

          
+
#tabpage(codelang=cs,container-title=Skipメソッド・Takeメソッドの実装)
+
#code{{
+
using System;
+
using System.Collections.Generic;
+

          
+
public static class Enumerable {
+
  public static IEnumerable<TSource> Skip<TSource>(this IEnumerable<TSource> source, int count)
+
  {
+
    var result = new List<TSource>(); // 結果を格納するリスト
+

          
+
    foreach (var e in source) {
+
      if (0 < count)
+
        count--;
+
      else
+
        result.Add(e); // 指定された要素数を列挙した時点から追加する
+
    }
+

          
+
    return result;
+
  }
+

          
+
  public static IEnumerable<TSource> Take<TSource>(this IEnumerable<TSource> source, int count)
+
  {
+
    var result = new List<TSource>(); // 結果を格納するリスト
+

          
+
    foreach (var e in source) {
+
      if (0 < count)
+
        result.Add(e); // 指定された要素数となるまで追加する
+
      else
+
        break; // 指定された要素数を超えたら列挙を中断してその時点までの結果を返す
+

          
+
      count--;
+
    }
+

          
+
    return result;
+
  }
+
}
+

          
+
class Sample {
+
  static void Main()
+
  {
+
    IEnumerable<int> source = new List<int>() {0, 1, 2, 3, 4};
+

          
+
    Console.WriteLine(string.Join(", ", source.Take(3)));
+

          
+
    Console.WriteLine(string.Join(", ", source.Skip(3)));
+
  }
+
}
+
}}
+
#tabpage(codelang=vb)
+
#code{{
+
Option Strict On
+
Option Infer On
+

          
+
Imports System
+
Imports System.Collections.Generic
+

          
+
Public Module Enumerable
+
  <System.Runtime.CompilerServices.Extension()> _
+
  Public Function Skip(Of TSource)(ByVal source As IEnumerable(Of TSource), ByVal count As Integer) As IEnumerable(Of TSource)
+
    Dim result As New List(Of TSource)() ' 結果を格納するリスト
+

          
+
    For Each e In source
+
      If 0 < count Then
+
        count -= 1
+
      Else
+
        result.Add(e) ' 指定された要素数を列挙した時点から追加する
+
      End If
+
    Next
+

          
+
    Return result
+
  End Function
+

          
+
  <System.Runtime.CompilerServices.Extension()> _
+
  Public Function Take(Of TSource)(ByVal source As IEnumerable(Of TSource), ByVal count As Integer) As IEnumerable(Of TSource)
+
    Dim result As New List(Of TSource)() ' 結果を格納するリスト
+

          
+
    For Each e In source
+
      If 0 < count Then
+
        result.Add(e) ' 指定された要素数となるまで追加する
+
      Else
+
        Exit For ' 指定された要素数を超えたら列挙を中断してその時点までの結果を返す
+
      End If
+

          
+
      count -= 1
+
    Next
+

          
+
    Return result
+
  End Function
+
End Module
+

          
+
Class Sample
+
  Shared Sub Main()
+
    Dim source As IEnumerable(Of Integer) = New List(Of Integer) From {0, 1, 2, 3, 4}
+

          
+
    ' 先頭から3つの要素を除いた残りの要素を取り出して表示する
+
    Console.WriteLine(String.Join(", ", source.Skip(3)))
+

          
+
    ' 先頭から3つの要素を取り出して表示する
+
    Console.WriteLine(String.Join(", ", source.Take(3)))
+
  End Sub
+
End Class
+
}}
+
#tabpage-end
+

          
+
ここで、Skipメソッド・Takeメソッドのドキュメントには
+

          
+
>このメソッドは遅延実行を使用して実装されます。
+
>(中略)
+
>このメソッドによって表されるクエリは、オブジェクトの GetEnumerator メソッドを直接呼び出すか、foreach (Visual C# の場合) または For Each (Visual Basic の場合) を使用することによってオブジェクトが列挙されるまで実行されません。
+

          
+
と記述されている。 これはSkipメソッド・Takeメソッドに限らず``IEnumerable<T>``を返すLINQメソッドに共通するものとなっている。
+

          
+
``IEnumerable<T>``の列挙結果を``yield``を使って生成するメソッドでは''遅延実行''が行われる。 先の実装では結果を``List<TSource>``に格納して返したが、かわりに``yield``によって逐次結果を返すようにする。 (VBではさらにメソッドに修飾子``Iterator``を付ける)
+

          
+
#tabpage(codelang=cs,container-title=yieldを使って遅延実行するようにする)
+
#code{{
+
using System;
+
using System.Collections.Generic;
+

          
+
public static class Enumerable {
+
  public static IEnumerable<TSource> Skip<TSource>(this IEnumerable<TSource> source, int count)
+
  {
+
    foreach (var e in source) {
+
      if (0 < count)
+
        count--;
+
      else
+
        yield return e; // yieldによって結果を返す
+
    }
+
  }
+

          
+
  public static IEnumerable<TSource> Take<TSource>(this IEnumerable<TSource> source, int count)
+
  {
+
    foreach (var e in source) {
+
      if (0 < count)
+
        yield return e; // yieldによって結果を返す
+
      else
+
        break;
+

          
+
      count--;
+
    }
+
  }
+
}
+

          
+
class Sample {
+
  static void Main()
+
  {
+
    IEnumerable<int> source = new List<int>() {0, 1, 2, 3, 4};
+

          
+
    Console.WriteLine(string.Join(", ", source.Skip(3)));
+

          
+
    Console.WriteLine(string.Join(", ", source.Take(3)));
+
  }
+
}
+
}}
+
#tabpage(codelang=vb)
+
#code{{
+
Option Strict On
+
Option Infer On
+

          
+
Imports System
+
Imports System.Collections.Generic
+

          
+
Public Module Enumerable
+
  <System.Runtime.CompilerServices.Extension()> _
+
  Public Iterator Function Skip(Of TSource)(ByVal source As IEnumerable(Of TSource), ByVal count As Integer) As IEnumerable(Of TSource)
+
    For Each e In source
+
      If 0 < count Then
+
        count -= 1
+
      Else
+
        Yield e ' Yieldによって結果を返す
+
      End If
+
    Next
+
  End Function
+

          
+
  <System.Runtime.CompilerServices.Extension()> _
+
  Public Iterator Function Take(Of TSource)(ByVal source As IEnumerable(Of TSource), ByVal count As Integer) As IEnumerable(Of TSource)
+
    For Each e In source
+
      If 0 < count Then
+
        Yield e ' yieldによって結果を返す
+
      Else
+
        Exit For
+
      End If
+

          
+
      count -= 1
+
    Next
+
  End Function
+
End Module
+

          
+
Class Sample
+
  Shared Sub Main()
+
    Dim source As IEnumerable(Of Integer) = New List(Of Integer) From {0, 1, 2, 3, 4}
+

          
+
    Console.WriteLine(String.Join(", ", source.Skip(3)))
+

          
+
    Console.WriteLine(String.Join(", ", source.Take(3)))
+
  End Sub
+
End Class
+
}}
+
#tabpage-end
+

          
+
これにより動作は変わらず、遅延実行するようになる。
+

          
+
#prompt(実行結果){{
+
3, 4
+
0, 1, 2
+
}}
+

          
+
ここまでの要点は
+
+Skip()などのソースシーケンスに加工して新たなシーケンス(``IEnumerable<T>``)を生み出すLINQメソッドでは、''遅延実行''が行われる
+
+''遅延実行''は``yield``によって実装することができる
+

          
+
*述語(predicate)を引数にとるメソッド [#predicate]
+
**Where [#Where]
+
&msdn(netfx,member,System.Linq.Enumerable.Where){Whereメソッド};は指定された条件(あるいは述語、''predicate'')に基づいてシーケンス内の要素を''フィルタ''して新たなシーケンスを生成することができる。 例えば偶数だけをフィルタするには次のようにする。
+

          
+
#tabpage(codelang=cs,container-title=Whereメソッドを使ったシーケンスのフィルタ)
+
#code{{
+
using System;
+
using System.Collections.Generic;
+
using System.Linq;
+

          
+
class Sample {
+
  static void Main()
+
  {
+
    IEnumerable<int> source = new List<int>() {0, 1, 2, 3, 4};
+

          
+
    Console.WriteLine(string.Join(", ", source.Where(val => val % 2 == 0))); // 偶数(=2で割った余りが0)の要素だけをフィルタする
+
  }
+
}
+
}}
+
#tabpage(codelang=vb)
+
#code{{
+
Option Strict On
+
Option Infer On
+

          
+
Imports System
+
Imports System.Collections.Generic
+
Imports System.Linq
+

          
+
Class Sample
+
  Shared Sub Main()
+
    Dim source As IEnumerable(Of Integer) = New List(Of Integer) From {0, 1, 2, 3, 4}
+

          
+
    Console.WriteLine(String.Join(", ", source.Where(Function(val) val Mod 2 = 0))) ' 偶数(=2で割った余りが0)の要素だけをフィルタする
+
  End Sub
+
End Class
+
}}
+
#tabpage-end
+

          
+
#prompt(実行結果){{
+
0, 2, 4
+
}}
+

          
+
フィルタの条件は、``TSource``型の要素を検査して``bool``を返すメソッドのデリゲート(``Func<TSource, bool>``)を引数&var{predicate};に渡すことによって指定する。 ラムダ式を使わずに条件を記述するとこのようになる。 ここでは要素の型``TSource``は``int``なので、&var{predicate};にはデリゲート``Func<int, bool>``を渡す。
+

          
+
#tabpage(codelang=cs,container-title=フィルタの条件を定義したメソッド)
+
#code{{
+
using System;
+
using System.Collections.Generic;
+
using System.Linq;
+

          
+
class Sample {
+
  // 値が偶数か否かを判定するメソッド
+
  static bool IsEven(int val)
+
  {
+
    if (val % 2 == 0)
+
      return true;
+
    else
+
      return false;
+
  }
+
  
+
  static void Main()
+
  {
+
    IEnumerable<int> source = new List<int>() {0, 1, 2, 3, 4};
+

          
+
    var predicate = (Func<int, bool>)IsEven; // フィルタの条件
+

          
+
    Console.WriteLine(string.Join(", ", source.Where(predicate)));
+
  }
+
}
+
}}
+
#tabpage(codelang=vb)
+
#code{{
+
Option Strict On
+
Option Infer On
+

          
+
Imports System
+
Imports System.Collections.Generic
+
Imports System.Linq
+

          
+
Class Sample
+
  ' 値が偶数か否かを判定するメソッド
+
  Shared Function IsEven(ByVal val As Integer) As Boolean
+
    If val Mod 2 = 0 Then
+
      Return True
+
    Else
+
      Return False
+
    End If
+
  End Function
+

          
+
  Shared Sub Main()
+
    Dim source As IEnumerable(Of Integer) = New List(Of Integer) From {0, 1, 2, 3, 4}
+

          
+
    Dim predicate As Func(Of Integer, Boolean) = AddressOf IsEven ' フィルタの条件
+

          
+
    Console.WriteLine(String.Join(", ", source.Where(predicate)))
+
  End Sub
+
End Class
+
}}
+
#tabpage-end
+

          
+
これをLINQを使わずに記述すると次のようになる。
+

          
+
#tabpage(codelang=cs,container-title=LINQを使わずにソースシーケンスをフィルタする)
+
#code{{
+
using System;
+
using System.Collections.Generic;
+

          
+
class Sample {
+
  // 値が偶数か否かを判定するメソッド
+
  static bool IsEven(int val)
+
  {
+
    if (val % 2 == 0)
+
      return true;
+
    else
+
      return false;
+
  }
+
  
+
  static void Main()
+
  {
+
    IEnumerable<int> source = new List<int>() {0, 1, 2, 3, 4};
+

          
+
    var predicate = (Func<int, bool>)IsEven; // フィルタの条件
+

          
+
    var result = new List<int>(); // フィルタした結果を代入するリスト
+

          
+
    foreach (var e in source) {
+
      if (predicate(e))
+
        result.Add(e); // 条件に一致する要素のみをリストに格納する
+
    }
+

          
+
    Console.WriteLine(string.Join(", ", result));
+
  }
+
}
+
}}
+
#tabpage(codelang=vb)
+
#code{{
+
Option Strict On
+
Option Infer On
+

          
+
Imports System
+
Imports System.Collections.Generic
+

          
+
Class Sample
+
  ' 値が偶数か否かを判定するメソッド
+
  Shared Function IsEven(ByVal val As Integer) As Boolean
+
    If val Mod 2 = 0 Then
+
      Return True
+
    Else
+
      Return False
+
    End If
+
  End Function
+

          
+
  Shared Sub Main()
+
    Dim source As IEnumerable(Of Integer) = New List(Of Integer) From {0, 1, 2, 3, 4}
+

          
+
    Dim predicate As Func(Of Integer, Boolean) = AddressOf IsEven ' フィルタの条件
+

          
+
    Dim result As New List(Of Integer)() ' フィルタした結果を代入するリスト
+

          
+
    For Each e In source
+
      If predicate(e) Then
+
        result.Add(e) ' 条件に一致する要素のみをリストに格納する
+
      End If
+
    Next
+

          
+
    Console.WriteLine(String.Join(", ", result))
+
  End Sub
+
End Class
+
}}
+
#tabpage-end
+

          
+
これまでの例と同様、このアルゴリズムを遅延実行するようにし、かつ拡張メソッドとして呼び出せるようにするとこのようになる。
+

          
+
#tabpage(codelang=cs,container-title=Whereメソッドの実装)
+
#code{{
+
using System;
+
using System.Collections.Generic;
+

          
+
public static class Enumerable {
+
  public static IEnumerable<TSource> Where<TSource>(this IEnumerable<TSource> source, Func<TSource, bool> predicate)
+
  {
+
    foreach (var e in source) {
+
      if (predicate(e))
+
        yield return e; // 条件に一致する要素のみをyieldによって返す
+
    }
+
  }
+
}
+

          
+
class Sample {
+
  // 値が偶数か否かを判定するメソッド
+
  static bool IsEven(int val)
+
  {
+
    if (val % 2 == 0)
+
      return true;
+
    else
+
      return false;
+
  }
+
  
+
  static void Main()
+
  {
+
    IEnumerable<int> source = new List<int>() {0, 1, 2, 3, 4};
+

          
+
    Console.WriteLine(string.Join(", ", source.Where(IsEven)));
+

          
+
    // 当然、ラムダ式を使って呼び出すこともできる
+
    Console.WriteLine(string.Join(", ", source.Where(val => val % 2 == 0)));
+
  }
+
}
+
}}
+
#tabpage(codelang=vb)
+
#code{{
+
Option Strict On
+
Option Infer On
+

          
+
Imports System
+
Imports System.Collections.Generic
+

          
+
Public Module Enumerable
+
  <System.Runtime.CompilerServices.Extension()> _
+
  Public Iterator Function Where(Of TSource)(ByVal source As IEnumerable(Of TSource), ByVal predicate As Func(Of TSource, Boolean)) As IEnumerable(Of TSource)
+
    For Each e In source
+
      If predicate(e) Then
+
        Yield e ' 条件に一致する要素のみをYieldによって返す
+
      End If
+
    Next
+
  End Function
+
End Module
+

          
+
Class Sample
+
  ' 値が偶数か否かを判定するメソッド
+
  Shared Function IsEven(ByVal val As Integer) As Boolean
+
    If val Mod 2 = 0 Then
+
      Return True
+
    Else
+
      Return False
+
    End If
+
  End Function
+

          
+
  Shared Sub Main()
+
    Dim source As IEnumerable(Of Integer) = New List(Of Integer) From {0, 1, 2, 3, 4}
+

          
+
    Console.WriteLine(String.Join(", ", source.Where(AddressOf IsEven)))
+

          
+
    ' 当然、ラムダ式を使って呼び出すこともできる
+
    Console.WriteLine(String.Join(", ", source.Where(Function(val) val Mod 2 = 0)))
+
  End Sub
+
End Class
+
}}
+
#tabpage-end
+

          
+

          
+
**All, Any [#All_Any]
+
&msdn(netfx,member,System.Linq.Enumerable.All){Allメソッド};はソースシーケンス内のすべての要素が条件を満たすかどうかを調べる。 また&msdn(netfx,member,System.Linq.Enumerable.Any){Anyメソッド};は条件を満たす要素が一つでもあるかどうかを調べる。
+

          
+
#tabpage(codelang=cs,container-title=Allメソッド・Anyメソッド)
+
#code{{
+
using System;
+
using System.Collections.Generic;
+
using System.Linq;
+

          
+
class Sample {
+
  // 値が偶数か否かを判定するメソッド
+
  static bool IsEven(int val)
+
  {
+
    if (val % 2 == 0)
+
      return true;
+
    else
+
      return false;
+
  }
+
  
+
  static void Main()
+
  {
+
    IEnumerable<int> source = new List<int>() {0, 1, 2, 3, 4};
+

          
+
    Console.WriteLine(source.All(IsEven)); // すべての要素が偶数かどうか
+
    Console.WriteLine(source.Any(IsEven)); // 偶数の要素を含むかどうか
+
  }
+
}
+
}}
+
#tabpage(codelang=vb)
+
#code{{
+
Option Strict On
+
Option Infer On
+

          
+
Imports System
+
Imports System.Collections.Generic
+
Imports System.Linq
+

          
+
Class Sample
+
  ' 値が偶数か否かを判定するメソッド
+
  Shared Function IsEven(ByVal val As Integer) As Boolean
+
    If val Mod 2 = 0 Then
+
      Return True
+
    Else
+
      Return False
+
    End If
+
  End Function
+

          
+
  Shared Sub Main()
+
    Dim source As IEnumerable(Of Integer) = New List(Of Integer) From {0, 1, 2, 3, 4}
+

          
+
    Console.WriteLine(source.All(AddressOf IsEven)) ' すべての要素が偶数かどうか
+
    Console.WriteLine(source.Any(AddressOf IsEven)) ' 偶数の要素を含むかどうか
+
  End Sub
+
End Class
+
}}
+
#tabpage-end
+

          
+
#prompt(実行結果){{
+
False
+
True
+
}}
+

          
+
これも[[Whereメソッドの場合>#Where]]と同様に``Func<TSource, bool>``を使って次のように実装することができる。
+

          
+
#tabpage(codelang=cs,container-title=Allメソッド・Anyメソッドの実装)
+
#code{{
+
using System;
+
using System.Collections.Generic;
+

          
+
public static class Enumerable {
+
  public static bool All<TSource>(this IEnumerable<TSource> source, Func<TSource, bool> predicate)
+
  {
+
    foreach (var e in source) {
+
      if (!predicate(e))
+
        return false; // 条件に一致しない要素があった時点でfalseを返す
+
    }
+

          
+
    return true; // すべて条件に一致する場合はtrueを返す
+
  }
+

          
+
  public static bool Any<TSource>(this IEnumerable<TSource> source, Func<TSource, bool> predicate)
+
  {
+
    foreach (var e in source) {
+
      if (predicate(e))
+
        return true; // 条件に一致する要素があった場合はtrueを返す
+
    }
+

          
+
    return false; // なければfalseを返す
+
  }
+
}
+

          
+
class Sample {
+
  // 値が偶数か否かを判定するメソッド
+
  static bool IsEven(int val)
+
  {
+
    if (val % 2 == 0)
+
      return true;
+
    else
+
      return false;
+
  }
+
  
+
  static void Main()
+
  {
+
    IEnumerable<int> source = new List<int>() {0, 1, 2, 3, 4};
+

          
+
    Console.WriteLine(source.All(IsEven)); // すべての要素が偶数かどうか
+
    Console.WriteLine(source.Any(IsEven)); // 偶数の要素を含むかどうか
+
  }
+
}
+
}}
+
#tabpage(codelang=vb)
+
#code{{
+
Option Strict On
+
Option Infer On
+

          
+
Imports System
+
Imports System.Collections.Generic
+

          
+
Public Module Enumerable
+
  <System.Runtime.CompilerServices.Extension()> _
+
  Public Function All(Of TSource)(ByVal source As IEnumerable(Of TSource), ByVal predicate As Func(Of TSource, Boolean)) As Boolean
+
    For Each e In source
+
      If Not predicate(e) Then
+
        Return False ' 条件に一致しない要素があった時点でFalseを返す
+
      End If
+
    Next
+

          
+
    Return True ' すべて条件に一致する場合はTrueを返す
+
  End Function
+

          
+
  <System.Runtime.CompilerServices.Extension()> _
+
  Public Function Any(Of TSource)(ByVal source As IEnumerable(Of TSource), ByVal predicate As Func(Of TSource, Boolean)) As Boolean
+
    For Each e In source
+
      If predicate(e) Then
+
        Return True ' 条件に一致する要素があった場合はTrueを返す
+
      End If
+
    Next
+

          
+
    Return False ' なければFalseを返す
+
  End Function
+
End Module
+

          
+
Class Sample
+
  ' 値が偶数か否かを判定するメソッド
+
  Shared Function IsEven(ByVal val As Integer) As Boolean
+
    If val Mod 2 = 0 Then
+
      Return True
+
    Else
+
      Return False
+
    End If
+
  End Function
+

          
+
  Shared Sub Main()
+
    Dim source As IEnumerable(Of Integer) = New List(Of Integer) From {0, 1, 2, 3, 4}
+

          
+
    Console.WriteLine(source.All(AddressOf IsEven)) ' すべての要素が偶数かどうか
+
    Console.WriteLine(source.Any(AddressOf IsEven)) ' 偶数の要素を含むかどうか
+
  End Sub
+
End Class
+
}}
+
#tabpage-end
+

          
+
**SkipWhile, TakeWhile [#SkipWhile_TakeWhile]
+
要素数を指定する[[Skipメソッド・Takeメソッド>#Skip_Take]]とは異なり、&msdn(netfx,member,System.Linq.Enumerable.SkipWhile){SkipWhileメソッド};・&msdn(netfx,member,System.Linq.Enumerable.TakeWhile){TakeWhileメソッド};では要素を選別する条件を``Func<TSource, bool>``で指定することができる。
+

          
+
#tabpage(codelang=cs,container-title=SkipWhileメソッド・TakeWhileメソッド)
+
#code{{
+
using System;
+
using System.Collections.Generic;
+
using System.Linq;
+

          
+
class Sample {
+
  static void Main()
+
  {
+
    IEnumerable<int> source = new List<int>() {0, 1, 2, 3, 4};
+

          
+
    Console.WriteLine(string.Join(", ", source.SkipWhile(val => val <= 2))); // 要素が2以下である間はスキップする
+
    Console.WriteLine(string.Join(", ", source.TakeWhile(val => val <= 2))); // 要素が2以下である間は要素を抽出する
+
  }
+
}
+
}}
+
#tabpage(codelang=vb)
+
#code{{
+
Option Strict On
+
Option Infer On
+

          
+
Imports System
+
Imports System.Collections.Generic
+
Imports System.Linq
+

          
+
Class Sample
+
  Shared Sub Main()
+
    Dim source As IEnumerable(Of Integer) = New List(Of Integer) From {0, 1, 2, 3, 4}
+

          
+
    Console.WriteLine(String.Join(", ", source.SkipWhile(Function(val) val <= 2))) ' 要素が2以下である間はスキップする
+
    Console.WriteLine(String.Join(", ", source.TakeWhile(Function(val) val <= 2))) ' 要素が2以下である間は要素を抽出する
+
  End Sub
+
End Class
+
}}
+
#tabpage-end
+

          
+
#prompt(実行結果){{
+
3, 4
+
0, 1, 2
+
}}
+

          
+
これも[[Allメソッド・Anyメソッド>#All_Any]]と同様に次のように実装できる。
+

          
+
#tabpage(codelang=cs,container-title=SkipWhileメソッド・TakeWhileメソッドの実装)
+
#code{{
+
using System;
+
using System.Collections.Generic;
+

          
+
public static class Enumerable {
+
  public static IEnumerable<TSource> TakeWhile<TSource>(this IEnumerable<TSource> source, Func<TSource, bool> predicate)
+
  {
+
    foreach (var e in source) {
+
      if (predicate(e))
+
        yield return e; // 要素が条件に一致する場合はその要素を返す
+
      else
+
        break; // 一致しなくなった時点で列挙を中断する
+
    }
+
  }
+

          
+
  public static IEnumerable<TSource> SkipWhile<TSource>(this IEnumerable<TSource> source, Func<TSource, bool> predicate)
+
  {
+
    bool skip = true;
+

          
+
    foreach (var e in source) {
+
      if (skip) {
+
        if (!predicate(e)) {
+
          yield return e; // 要素が条件に一致しなくなった時点でその要素を返す
+
          skip = false; // 以降の要素をすべて返す
+
        }
+
      }
+
      else {
+
        yield return e;
+
      }
+
    }
+
  }
+
}
+

          
+
class Sample {
+
  static void Main()
+
  {
+
    IEnumerable<int> source = new List<int>() {0, 1, 2, 3, 4};
+

          
+
    Console.WriteLine(string.Join(", ", source.SkipWhile(val => val <= 2))); // 要素が2以下である間はスキップする
+
    Console.WriteLine(string.Join(", ", source.TakeWhile(val => val <= 2))); // 要素が2以下である間は要素を抽出する
+
  }
+
}
+
}}
+
#tabpage(codelang=vb)
+
#code{{
+
Option Strict On
+
Option Infer On
+

          
+
Imports System
+
Imports System.Collections.Generic
+

          
+
Public Module Enumerable
+
  <System.Runtime.CompilerServices.Extension()> _
+
  Public Iterator Function TakeWhile(Of TSource)(ByVal source As IEnumerable(Of TSource), ByVal predicate As Func(Of TSource, Boolean)) As IEnumerable(Of TSource)
+
    For Each e In source
+
      If predicate(e) Then
+
        Yield e ' 要素が条件に一致する場合はその要素を返す
+
      Else
+
        Exit For ' 一致しなくなった時点で列挙を中断する
+
      End If
+
    Next
+
  End Function
+

          
+
  <System.Runtime.CompilerServices.Extension()> _
+
  Public Iterator Function SkipWhile(Of TSource)(ByVal source As IEnumerable(Of TSource), ByVal predicate As Func(Of TSource, Boolean)) As IEnumerable(Of TSource)
+
    Dim skip As Boolean = True
+

          
+
    For Each e In source
+
      If skip Then
+
        If Not predicate(e) Then
+
          Yield e ' 要素が条件に一致しなくなった時点でその要素を返す
+
          skip = False ' 以降の要素をすべて返す
+
        End If
+
      Else
+
        Yield e
+
      End If
+
    Next
+
  End Function
+
End Module
+

          
+
Class Sample
+
  Shared Sub Main()
+
    Dim source As IEnumerable(Of Integer) = New List(Of Integer) From {0, 1, 2, 3, 4}
+

          
+
    Console.WriteLine(String.Join(", ", source.SkipWhile(Function(val) val <= 2))) ' 要素が2以下である間はスキップする
+
    Console.WriteLine(String.Join(", ", source.TakeWhile(Function(val) val <= 2))) ' 要素が2以下である間は要素を抽出する
+
  End Sub
+
End Class
+

          
+
}}
+
#tabpage-end
+

          
+
#prompt(実行結果){{
+
3, 4
+
0, 1, 2
+
}}
+

          
+
*変換関数(selector)を引数にとるメソッド [#selector]
+
**Select [#Select]
+
&msdn(netfx,member,System.Linq.Enumerable.Select){Selectメソッド};は指定された''変換関数''(``Func<TSource, TResult>``)に基づいてシーケンス内の全要素を変換し、新しいシーケンスを返す。 変換関数(``Convert<TInput, TOutput>``)を使って配列の型を変換する&msdn(netfx,member,System.Array.ConvertAll){Array.ConvertAllメソッド};と似ている。 ([[programming/netfx/arrays/2_operations#Array.ConvertAll]])
+

          
+
次の例では、クラスから特定のフィールドを''選択''する変換関数を指定することにより、クラス型のシーケンスから各フィールドの値を抽出したシーケンスを生成している。
+

          
+
#tabpage(codelang=cs,container-title=Selectメソッド)
+
#code{{
+
using System;
+
using System.Collections.Generic;
+
using System.Linq;
+

          
+
class Account {
+
  public int ID;
+
  public string Name;
+

          
+
  public override string ToString()
+
  {
+
    return string.Format("{0}:{1}", ID, Name);
+
  }
+
}
+

          
+
class Sample {
+
  static void Main()
+
  {
+
    IEnumerable<Account> source = new List<Account>() {
+
      new Account() {ID = 0, Name = "Alice"},
+
      new Account() {ID = 1, Name = "Bob"},
+
      new Account() {ID = 2, Name = "Charlie"},
+
    };
+

          
+
    Console.WriteLine(string.Join(", ", source));
+

          
+
    // シーケンス内の各要素からNameプロパティの値だけを'選択'する
+
    var names = source.Select(account => account.Name);
+

          
+
    Console.WriteLine(string.Join(", ", names));
+

          
+
    // シーケンス内の各要素からIDプロパティの値だけを'選択'する
+
    var ids = source.Select(account => account.ID);
+

          
+
    Console.WriteLine(string.Join(", ", ids));
+
  }
+
}
+
}}
+
#tabpage(codelang=vb)
+
#code{{
+
Option Strict On
+
Option Infer On
+

          
+
Imports System
+
Imports System.Collections.Generic
+
Imports System.Linq
+

          
+
Class Account
+
  Public ID As Integer
+
  Public Name As String
+

          
+
  Public Overrides Function ToString() As String
+
    Return String.Format("{0}:{1}", ID, Name)
+
  End Function
+
End Class
+

          
+
Class Sample
+
  Shared Sub Main()
+
    Dim source As IEnumerable(Of Account) = New List(Of Account) From { _
+
      New Account With {.ID = 0, .Name = "Alice"}, _
+
      New Account With {.ID = 1, .Name = "Bob"}, _
+
      New Account With {.ID = 2, .Name = "Charlie"} _
+
    }
+

          
+
    Console.WriteLine(String.Join(", ", source))
+

          
+
    ' シーケンス内の各要素からNameプロパティの値だけを'選択'する
+
    Dim names = source.Select(Function(account) account.Name)
+

          
+
    Console.WriteLine(String.Join(", ", names))
+

          
+
    ' シーケンス内の各要素からIDプロパティの値だけを'選択'する
+
    Dim ids = source.Select(Function(account) account.ID)
+

          
+
    Console.WriteLine(String.Join(", ", ids))
+
  End Sub
+
End Class
+
}}
+
#tabpage-end
+

          
+
#prompt(実行結果){{
+
0:Alice, 1:Bob, 2:Charlie
+
Alice, Bob, Charlie
+
0, 1, 2
+
}}
+

          
+
もうひとつ別の例として、シーケンス内の全要素の平方根を求めるには次のようにする。
+

          
+
#tabpage(codelang=cs,container-title=Selectメソッドを使ってシーケンスの平方根を求める例)
+
#code{{
+
using System;
+
using System.Collections.Generic;
+
using System.Linq;
+

          
+
class Sample {
+
  static void Main()
+
  {
+
    IEnumerable<int> source = new List<int>() {0, 1, 2, 3, 4};
+

          
+
    Console.WriteLine(string.Join(", ", source.Select(val => Math.Sqrt(val))));
+
  }
+
}
+
}}
+
#tabpage(codelang=vb)
+
#code{{
+
Option Strict On
+
Option Infer On
+

          
+
Imports System
+
Imports System.Collections.Generic
+
Imports System.Linq
+

          
+
Class Sample
+
  Shared Sub Main()
+
    Dim source As IEnumerable(Of Integer) = New List(Of Integer) From {0, 1, 2, 3, 4}
+

          
+
    Console.WriteLine(String.Join(", ", source.Select(Function(val) Math.Sqrt(val))))
+
  End Sub
+
End Class
+
}}
+
#tabpage-end
+

          
+
#prompt(実行結果){{
+
0, 1, 1.4142135623731, 1.73205080756888, 2
+
}}
+

          
+
Selectメソッドでは、変換関数(``Func<TSource, TResult>``)を引数&var{selector};で指定する。 変換関数の引数の型は元のシーケンスと同じ型(``TSource``)で、戻り値は変換後の型(``TResult``)となる。 ラムダ式を使わずに変換関数を記述するとこのようになる。
+

          
+
#tabpage(codelang=cs,container-title=ラムダ式を使わずに変換関数を記述した例)
+
#code{{
+
using System;
+
using System.Collections.Generic;
+
using System.Linq;
+

          
+
class Sample {
+
  // 与えられた値をその平方根に変換するメソッド
+
  static double Sqrt(int val)
+
  {
+
    return Math.Sqrt(val);
+
  }
+

          
+
  static void Main()
+
  {
+
    IEnumerable<int> source = new List<int>() {0, 1, 2, 3, 4};
+

          
+
    var selector = (Func<int, double>)Sqrt; // intの値をdoubleに変換する変換関数
+

          
+
    Console.WriteLine(string.Join(", ", source.Select(selector)));
+
  }
+
}
+
}}
+
#tabpage(codelang=vb)
+
#code{{
+
Option Strict On
+
Option Infer On
+

          
+
Imports System
+
Imports System.Collections.Generic
+
Imports System.Linq
+

          
+
Class Sample
+
  ' 与えられた値をその平方根に変換するメソッド
+
  Shared Function Sqrt(ByVal val As Integer) As Double
+
    Return Math.Sqrt(val)
+
  End Function
+

          
+
  Shared Sub Main()
+
    Dim source As IEnumerable(Of Integer) = New List(Of Integer) From {0, 1, 2, 3, 4}
+

          
+
    Dim selector As Func(Of Integer, Double) = AddressOf Sqrt ' Integerの値をDoubleに変換する変換関数
+

          
+
    Console.WriteLine(String.Join(", ", source.Select(selector)))
+
  End Sub
+
End Class
+
}}
+
#tabpage-end
+

          
+
これをLINQを使わずに記述すると次のようになる。
+

          
+
#tabpage(codelang=cs,container-title=LINQを使わずにソースシーケンスを変換する例)
+
#code{{
+
using System;
+
using System.Collections.Generic;
+

          
+
class Sample {
+
  // 与えられた値をその平方根に変換するメソッド
+
  static double Sqrt(int val)
+
  {
+
    return Math.Sqrt(val);
+
  }
+

          
+
  static void Main()
+
  {
+
    IEnumerable<int> source = new List<int>() {0, 1, 2, 3, 4};
+

          
+
    var selector = (Func<int, double>)Sqrt; // 変換関数
+

          
+
    var result = new List<double>(); // 変換した結果を代入するリスト
+

          
+
    foreach (var e in source) {
+
      result.Add(selector(e)); // 列挙された要素に変換関数を適用してリストに追加する
+
    }
+

          
+
    Console.WriteLine(string.Join(", ", result));
+
  }
+
}
+
}}
+
#tabpage(codelang=vb)
+
#code{{
+
Option Strict On
+
Option Infer On
+

          
+
Imports System
+
Imports System.Collections.Generic
+

          
+
Class Sample
+
  ' 与えられた値をその平方根に変換するメソッド
+
  Shared Function Sqrt(ByVal val As Integer) As Double
+
    Return Math.Sqrt(val)
+
  End Function
+

          
+
  Shared Sub Main()
+
    Dim source As IEnumerable(Of Integer) = New List(Of Integer) From {0, 1, 2, 3, 4}
+

          
+
    Dim selector As Func(Of Integer, Double) = AddressOf Sqrt ' 変換関数
+

          
+
    Dim result As New List(Of Double)() ' 変換した結果を代入するリスト
+

          
+
    For Each e In source
+
      result.Add(selector(e)) ' 列挙された要素に変換関数を適用してリストに追加する
+
    Next
+

          
+
    Console.WriteLine(String.Join(", ", result))
+
  End Sub
+
End Class
+
}}
+
#tabpage-end
+

          
+
これまでの例と同様、このアルゴリズムを遅延実行するようにし、かつ拡張メソッドとして呼び出せるようにするとこのようになる。
+

          
+
#tabpage(codelang=cs,container-title=Selectメソッドの実装)
+
#code{{
+
using System;
+
using System.Collections.Generic;
+

          
+
public static class Enumerable {
+
  public static IEnumerable<TResult> Select<TSource, TResult>(this IEnumerable<TSource> source, Func<TSource, TResult> selector)
+
  {
+
    foreach (var e in source) {
+
      yield return selector(e); // 変換関数を適用した結果をyieldによって返す
+
    }
+
  }
+
}
+

          
+
class Sample {
+
  // 与えられた値をその平方根に変換するメソッド
+
  static double Sqrt(int val)
+
  {
+
    return Math.Sqrt(val);
+
  }
+

          
+
  static void Main()
+
  {
+
    IEnumerable<int> source = new List<int>() {0, 1, 2, 3, 4};
+

          
+
    var selector = (Func<int, double>)Sqrt; // 変換関数
+

          
+
    Console.WriteLine(string.Join(", ", source.Select(selector)));
+

          
+
    // 当然、ラムダ式を使って呼び出すこともできる
+
    Console.WriteLine(string.Join(", ", source.Select(val => Math.Sqrt(val))));
+
  }
+
}
+
}}
+
#tabpage(codelang=vb)
+
#code{{
+
Option Strict On
+
Option Infer On
+

          
+
Imports System
+
Imports System.Collections.Generic
+

          
+
Public Module Enumerable
+
  <System.Runtime.CompilerServices.Extension()> _
+
  Public Iterator Function [Select](Of TSource, TResult)(ByVal source As IEnumerable(Of TSource), ByVal selector As Func(Of TSource, TResult)) As IEnumerable(Of TResult)
+
    For Each e In source
+
      Yield selector(e) ' 変換関数を適用した結果をYieldによって返す
+
    Next
+
  End Function
+
End Module
+

          
+
Class Sample
+
  ' 与えられた値をその平方根に変換するメソッド
+
  Shared Function Sqrt(ByVal val As Integer) As Double
+
    Return Math.Sqrt(val)
+
  End Function
+

          
+
  Shared Sub Main()
+
    Dim source As IEnumerable(Of Integer) = New List(Of Integer) From {0, 1, 2, 3, 4}
+

          
+
    Dim selector As Func(Of Integer, Double) = AddressOf Sqrt ' 変換関数
+

          
+
    Console.WriteLine(String.Join(", ", source.Select(selector)))
+

          
+
    ' 当然、ラムダ式を使って呼び出すこともできる
+
    Console.WriteLine(String.Join(", ", source.Select(Function(val) Math.Sqrt(val))))
+
  End Sub
+
End Class
+
}}
+
#tabpage-end
+

          
+

          
+
**Average, Sum [#Average_Sum]
+
&msdn(netfx,member,System.Linq.Enumerable.Average){Averageメソッド};・&msdn(netfx,member,System.Linq.Enumerable.Sum){Sumメソッド};は変換関数によって平均・合計として計上する値を各要素から''選択''する。 これによりシーケンス内の全要素から特定の値に基づいた平均・合計を求めることができる。
+

          
+
#tabpage(codelang=cs,container-title=Averageメソッド・Sumメソッド)
+
#code{{
+
using System;
+
using System.Collections.Generic;
+
using System.Linq;
+

          
+
class File {
+
  public string Name;
+
  public int Length;
+
}
+

          
+
class Sample {
+
  static void Main()
+
  {
+
    IEnumerable<File> source = new List<File>() {
+
      new File() {Name = "sample.txt", Length = 123},
+
      new File() {Name = "sample.jpg", Length = 45679},
+
      new File() {Name = "sample.zip", Length = 3145},
+
    };
+

          
+
    Console.WriteLine("Average = {0}", source.Average(file => file.Length)); // Lengthの平均を求める
+

          
+
    Console.WriteLine("Sum = {0}", source.Sum(file => file.Length)); // Lengthの合計を求める
+
  }
+
}
+
}}
+
#tabpage(codelang=vb)
+
#code{{
+
Option Strict On
+
Option Infer On
+

          
+
Imports System
+
Imports System.Collections.Generic
+
Imports System.Linq
+

          
+
Class File
+
  Public Name As String
+
  Public Length As Integer
+
End Class
+

          
+
Class Sample
+
  Shared Sub Main()
+
    Dim source As IEnumerable(Of File) = New List(Of File) From { _
+
      New File() With {.Name = "sample.txt", .Length = 123}, _
+
      New File() With {.Name = "sample.jpg", .Length = 45679}, _
+
      New File() With {.Name = "sample.zip", .Length = 3145} _
+
    }
+

          
+
    Console.WriteLine("Average = {0}", source.Average(Function(file) file.Length)) ' Lengthの平均を求める
+

          
+
    Console.WriteLine("Sum = {0}", source.Sum(Function(file) file.Length)) ' Lengthの合計を求める
+
  End Sub
+
End Class
+
}}
+
#tabpage-end
+

          
+
#prompt(実行結果){{
+
Average = 16315.6666666667
+
Sum = 48947
+
}}
+

          
+
これをLINQを使わずに記述すると次のようになる。
+

          
+
#tabpage(codelang=cs,container-title=LINQを使わずに平均・合計を求める例)
+
#code{{
+
using System;
+
using System.Collections.Generic;
+

          
+
class File {
+
  public string Name;
+
  public int Length;
+
}
+

          
+
class Sample {
+
  // FileクラスのLengthフィールドの値を参照・取得するメソッド
+
  static int GetLength(File file)
+
  {
+
    return file.Length;
+
  }
+

          
+
  static void Main()
+
  {
+
    IEnumerable<File> source = new List<File>() {
+
      new File() {Name = "sample.txt", Length = 123},
+
      new File() {Name = "sample.jpg", Length = 45679},
+
      new File() {Name = "sample.zip", Length = 3145},
+
    };
+

          
+
    var selector = (Func<File, int>)GetLength; // 合計を求めるための値を選択するための変換関数
+

          
+
    int sum = 0;
+
    int count = 0;
+

          
+
    foreach (var e in source) {
+
      sum += selector(e); // 要素から合計として加算する値を取得する
+
      count++;
+
    }
+

          
+
    Console.WriteLine("Average = {0}", sum / (double)count);
+

          
+
    Console.WriteLine("Sum = {0}", sum);
+
  }
+
}
+
}}
+
#tabpage(codelang=vb)
+
#code{{
+
Option Strict On
+
Option Infer On
+

          
+
Imports System
+
Imports System.Collections.Generic
+

          
+
Class File
+
  Public Name As String
+
  Public Length As Integer
+
End Class
+

          
+
Class Sample
+
  ' FileクラスのLengthフィールドの値を参照・取得するメソッド
+
  Shared Function GetLength(ByVal file As File) As Integer
+
    Return file.Length
+
  End Function
+

          
+
  Shared Sub Main()
+
    Dim source As IEnumerable(Of File) = New List(Of File) From { _
+
      New File() With {.Name = "sample.txt", .Length = 123}, _
+
      New File() With {.Name = "sample.jpg", .Length = 45679}, _
+
      New File() With {.Name = "sample.zip", .Length = 3145} _
+
    }
+

          
+
    Dim selector As Func(Of File, Integer) = AddressOf GetLength ' 合計を求めるための値を選択するための変換関数
+

          
+
    Dim sum As Integer = 0
+
    Dim count As Integer = 0
+

          
+
    For Each e In source
+
      sum += selector(e)
+
      count += 1
+
    Next
+

          
+
    Console.WriteLine("Average = {0}", sum / CDbl(count)) ' Lengthの平均を求める
+

          
+
    Console.WriteLine("Sum = {0}", sum) ' Lengthの合計を求める
+
  End Sub
+
End Class
+
}}
+
#tabpage-end
+

          
+
これまでの例と同様、このアルゴリズムを遅延実行するようにし、かつ拡張メソッドとして呼び出せるようにするとこのようになる。
+

          
+
#tabpage(codelang=cs,container-title=Averageメソッド・Sumメソッドの実装)
+
#code{{
+
using System;
+
using System.Collections.Generic;
+

          
+
public static class Enumerable {
+
  public static double Average<TSource>(this IEnumerable<TSource> source, Func<TSource, int> selector)
+
  {
+
    int sum = 0;
+
    int count = 0;
+

          
+
    foreach (var e in source) {
+
      sum += selector(e);
+
      count++;
+
    }
+

          
+
    if (count == 0)
+
      throw new InvalidOperationException("ソースシーケンスが空です");
+
    else
+
      return sum / (double)count;
+
  }
+

          
+
  public static int Sum<TSource>(this IEnumerable<TSource> source, Func<TSource, int> selector)
+
  {
+
    int sum = 0;
+

          
+
    foreach (var e in source) {
+
      sum += selector(e);
+
    }
+

          
+
    return sum;
+
  }
+
}
+

          
+
class File {
+
  public string Name;
+
  public int Length;
+
}
+

          
+
class Sample {
+
  // FileクラスのLengthフィールドの値を参照・取得するメソッド
+
  static int GetLength(File file)
+
  {
+
    return file.Length;
+
  }
+

          
+
  static void Main()
+
  {
+
    IEnumerable<File> source = new List<File>() {
+
      new File() {Name = "sample.txt", Length = 123},
+
      new File() {Name = "sample.jpg", Length = 45679},
+
      new File() {Name = "sample.zip", Length = 3145},
+
    };
+

          
+
    var selector = (Func<File, int>)GetLength; // 合計を求めるための値を選択するための変換関数
+

          
+
    Console.WriteLine("Average = {0}", source.Average(selector)); // Lengthの平均を求める
+

          
+
    Console.WriteLine("Sum = {0}", source.Sum(selector)); // Lengthの合計を求める
+

          
+
    // 当然、ラムダ式を使って呼び出すこともできる
+
    Console.WriteLine("Average = {0}", source.Average(file => file.Length));
+

          
+
    Console.WriteLine("Sum = {0}", source.Sum(file => file.Length));
+
  }
+
}
+
}}
+
#tabpage(codelang=vb)
+
#code{{
+
Option Strict On
+
Option Infer On
+

          
+
Imports System
+
Imports System.Collections.Generic
+

          
+
Public Module Enumerable
+
  <System.Runtime.CompilerServices.Extension()> _
+
  Public Function Average(Of TSource)(ByVal source As IEnumerable(Of TSource), ByVal selector As Func(Of TSource, Integer)) As Double
+
    Dim sum As Integer = 0
+
    Dim count As Integer = 0
+

          
+
    For Each e In source
+
      sum += selector(e)
+
      count += 1
+
    Next
+

          
+
    If count = 0 Then
+
      Throw New InvalidOperationException("ソースシーケンスが空です")
+
    Else
+
      Return sum / CDbl(count)
+
    End If
+
  End Function
+

          
+
  <System.Runtime.CompilerServices.Extension()> _
+
  Public Function Sum(Of TSource)(ByVal source As IEnumerable(Of TSource), ByVal selector As Func(Of TSource, Integer)) As Integer
+
    Dim s As Integer = 0
+

          
+
    For Each e In source
+
      s += selector(e)
+
    Next
+

          
+
    Return s
+
  End Function
+
End Module
+

          
+
Class File
+
  Public Name As String
+
  Public Length As Integer
+
End Class
+

          
+
Class Sample
+
  ' FileクラスのLengthフィールドの値を参照・取得するメソッド
+
  Shared Function GetLength(ByVal file As File) As Integer
+
    Return file.Length
+
  End Function
+

          
+
  Shared Sub Main()
+
    Dim source As IEnumerable(Of File) = New List(Of File) From { _
+
      New File() With {.Name = "sample.txt", .Length = 123}, _
+
      New File() With {.Name = "sample.jpg", .Length = 45679}, _
+
      New File() With {.Name = "sample.zip", .Length = 3145} _
+
    }
+

          
+
    Dim selector As Func(Of File, Integer) = AddressOf GetLength ' 合計を求めるための値を選択するための変換関数
+

          
+
    Console.WriteLine("Average = {0}", source.Average(selector)) ' Lengthの平均を求める
+

          
+
    Console.WriteLine("Sum = {0}", source.Sum(selector)) ' Lengthの合計を求める
+

          
+
    ' 当然、ラムダ式を使って呼び出すこともできる
+
    Console.WriteLine("Average = {0}", source.Average(Function(file) file.Length))
+

          
+
    Console.WriteLine("Sum = {0}", source.Sum(Function(file) file.Length))
+
  End Sub
+
End Class
+
}}
+
#tabpage-end
+

          
+

          
+

          
+

          
+
//*比較子(comparer)を引数にとるメソッド
+
//**Contains
+
//**SequenceEqual
+
//**Distinct
+

          
+

          
+