Foreachがどのように実現されるかについて
4780 ワード
foreachがどのように内部でこの問題を実現するかを検討する前に、2つのC#の中のインタフェース、IEnumerableとIEnumeratorを理解する必要があります.C#内のループセットで使用される関連クラスでは、IEnumerableが最も基本的なインタフェースです.これはIEnumerableのような汎用化可能なインタフェースです.マイクロソフトのNETがこの2つのインタフェースを発売してから、foreachの使い方ができました.foreachはこの2つのインタフェースの基礎の上に構築されていると言えます.foreachの前提は、その中の容器がIEnumerableインタフェースを実現することです.
IEnumerableというインタフェースで定義されている内容は非常に簡単で、最も重要なのは抽象的な方法があることです.IEnumerableとは、この集合が遍歴可能であることを意味し、このGetEnumeratorメソッドが返すIEnumeratorは、このツールを使用してクラスを遍歴する遍歴器である.IEnumerableがシャンパンなら、IEnumeratorは開瓶器です.このIEnumerableインタフェースを実装するには、このGetEnumeratorメソッドを実装し、インスタンス化されたIEnumoratorを返さなければならない.
このIEnumoratorインタフェースをご紹介します.このインタフェースで定義する内容も簡単で、Currentを含めて、この遍歴ツールが指すコンテナの現在の要素を返します.MoveNextメソッドは次の要素を指し、最後に要素がないまで遍歴するとfalseを返します.IEnumerableクラスを実現するとき、私たちの目的はこの集合を遍歴することです.だから、同時にIEnumeratorというツールクラスを実現し、CLRにこの集合を遍歴する方法を教えるために、私たちの論理を定義します.
次は簡単な例で、この2つのインタフェースの使い方を説明します.
もしforeachバックグラウンドの論理がこのように実現されたとしたら?多分こんな感じです.上のコードはCLRによってこのように翻訳されます.
追加:IEnumerableとORMフレームワークの併用時の遅延ロードの問題、およびResharperがこのインタフェースmutiple enumerationに警告する問題について
IEnumerableを使うとき、Resharperはよくこの問題を提示しますか?この問題は、この集合オブジェクトが異なるループ結果を返す可能性があることを意味します.
IEnumerableのもう一つの機能はSQLクラスのクエリーロジックを格納することであるため、ここでは本当のクエリー結果、つまり遅延ロードではなくクエリーロジックを指すことに注意してください.次のエッジの例を例に挙げると、objectsにSQLクエリーロジックが格納されている可能性があります.Any()メソッドが最初に呼び出されると、SQL文がデータベースにクエリされた結果が呼び出され、データが表示されます.しかしobjectsがFirst()メソッドを呼び出してこのレコードを取得した場合、この時点でデータベースが他のプログラムに変更され、データがない場合、dirty dataの問題が発生する可能性があります.したがって、データの整合性のためには、IEnumeralbeを使用する際に呼び出す必要がある.ToList()メソッドは,これらの内容を個々の実在する容器に格納することで,前後が一致する.
また、コード性能の観点から、毎回データベースを呼び出すのは遅いので、いっそすべてのデータベースの条件に合った結果をメモリリストに入れて、使うときに直接メモリから取るとずっと速くなります.
IEnumerable
いくつかの
ORM
フレームワークでは、遅延ロード機能が実現されています.たとえば,フレームが独自に定義したコンテナオブジェクトでは,特定の
IEnumerator
インタフェース
M
oveNext
で指定します.
IEnumerableというインタフェースで定義されている内容は非常に簡単で、最も重要なのは抽象的な方法があることです.IEnumerableとは、この集合が遍歴可能であることを意味し、このGetEnumeratorメソッドが返すIEnumeratorは、このツールを使用してクラスを遍歴する遍歴器である.IEnumerableがシャンパンなら、IEnumeratorは開瓶器です.このIEnumerableインタフェースを実装するには、このGetEnumeratorメソッドを実装し、インスタンス化されたIEnumoratorを返さなければならない.
このIEnumoratorインタフェースをご紹介します.このインタフェースで定義する内容も簡単で、Currentを含めて、この遍歴ツールが指すコンテナの現在の要素を返します.MoveNextメソッドは次の要素を指し、最後に要素がないまで遍歴するとfalseを返します.IEnumerableクラスを実現するとき、私たちの目的はこの集合を遍歴することです.だから、同時にIEnumeratorというツールクラスを実現し、CLRにこの集合を遍歴する方法を教えるために、私たちの論理を定義します.
次は簡単な例で、この2つのインタフェースの使い方を説明します.
// Person 。
public class Person
{
public Person(string fName, string lName)
{
this.firstName = fName;
this.lastName = lName;
}
public string firstName;
public string lastName;
}
//People Person , 。
//IEnumerable , GetEnumerator , IEnumerator 。
public class People : IEnumerable
{
private Person[] _people;
public People(Person[] pArray)
{
_people = new Person[pArray.Length];
for (int i = 0; i < pArray.Length; i++)
{
_people[i] = pArray[i];
}
}
public PeopleEnum GetEnumerator()
{
return new PeopleEnum(_people);
}
}
// 。
public class PeopleEnum : IEnumerator
{
public Person[] _people;
int position = -1;
public PeopleEnum(Person[] list)
{
_people = list;
}
public bool MoveNext()
{
position++;
return (position < _people.Length);
}
public void Reset()
{
position = -1;
}
object IEnumerator.Current
{
get
{
return Current;
}
}
public Person Current
{
get
{
try
{
return _people[position];
}
catch (IndexOutOfRangeException)
{
throw new InvalidOperationException();
}
}
}
}
class App
{
static void Main()
{
Person[] peopleArray = new Person[3]
{
new Person("John", "Smith"),
new Person("Jim", "Johnson"),
new Person("Sue", "Rabon"),
};
People peopleList = new People(peopleArray);
// foreach , IEnumerable 。
foreach (Person p in peopleList)
Console.WriteLine(p.firstName + " " + p.lastName);
}
}
/* This code produces output similar to the following:
*
* John Smith
* Jim Johnson
* Sue Rabon
*
*/
もしforeachバックグラウンドの論理がこのように実現されたとしたら?多分こんな感じです.上のコードはCLRによってこのように翻訳されます.
foreach (Person p in peopleList)
Console.WriteLine(p.firstName + " " + p.lastName);
//
IEnumerator enumerator = (peopleList).GetEnumerator();
try {
while (enumerator.MoveNext()) {
Person element; //post C# 5
element = (Person )enumerator.Current;
// foreach
Console.WriteLine(p.firstName + " " + p.lastName);
}
}
finally {
IDisposable disposable = enumerator as System.IDisposable;
if (disposable != null) disposable.Dispose();
}
追加:IEnumerableとORMフレームワークの併用時の遅延ロードの問題、およびResharperがこのインタフェースmutiple enumerationに警告する問題について
IEnumerableを使うとき、Resharperはよくこの問題を提示しますか?この問題は、この集合オブジェクトが異なるループ結果を返す可能性があることを意味します.
IEnumerableのもう一つの機能はSQLクラスのクエリーロジックを格納することであるため、ここでは本当のクエリー結果、つまり遅延ロードではなくクエリーロジックを指すことに注意してください.次のエッジの例を例に挙げると、objectsにSQLクエリーロジックが格納されている可能性があります.Any()メソッドが最初に呼び出されると、SQL文がデータベースにクエリされた結果が呼び出され、データが表示されます.しかしobjectsがFirst()メソッドを呼び出してこのレコードを取得した場合、この時点でデータベースが他のプログラムに変更され、データがない場合、dirty dataの問題が発生する可能性があります.したがって、データの整合性のためには、IEnumeralbeを使用する際に呼び出す必要がある.ToList()メソッドは,これらの内容を個々の実在する容器に格納することで,前後が一致する.
また、コード性能の観点から、毎回データベースを呼び出すのは遅いので、いっそすべてのデータベースの条件に合った結果をメモリリストに入れて、使うときに直接メモリから取るとずっと速くなります.
public List
IEnumerable
いくつかの
ORM
フレームワークでは、遅延ロード機能が実現されています.たとえば,フレームが独自に定義したコンテナオブジェクトでは,特定の
IEnumerator
インタフェース
M
oveNext
で指定します.