誰かがIEnumerableとIEnumeratorを私に説明できますか?
たとえば、foreachでいつ使用するのですか?IEnumerableとIEnumeratorの違いは何ですか?なぜそれを使用する必要があるのですか?
誰かがIEnumerableとIEnumeratorを私に説明できますか?
たとえば、foreachでいつ使用するのですか?IEnumerableとIEnumeratorの違いは何ですか?なぜそれを使用する必要があるのですか?
回答:
たとえば、foreachでいつ使用するのですか?
IEnumerable
"over" は使用しませんforeach
。実装IEnumerable
することでforeach
可能になります。
次のようなコードを書く場合:
foreach (Foo bar in baz)
{
...
}
機能的には次のように記述します。
IEnumerator bat = baz.GetEnumerator();
while (bat.MoveNext())
{
bar = (Foo)bat.Current
...
}
「機能的に同等」とは、コンパイラが実際にコードを変換することです。を実装しない限り、この例ではforeach
on baz
を使用できません。 baz
IEnumerable
IEnumerable
baz
メソッドを実装することを意味します
IEnumerator GetEnumerator()
IEnumerator
このメソッドが返すメソッドを実装しなければならないことをオブジェクト
bool MoveNext()
そして
Object Current()
最初のメソッドIEnumerable
は、列挙子を作成したオブジェクトの次のオブジェクトに進み、false
完了した場合は戻り、2番目のメソッドは現在のオブジェクトを返します。
実装で反復できる.NetのすべてのものIEnumerable
。独自のクラスを構築していて、それがを実装するクラスからまだ継承していないIEnumerable
場合はforeach
、実装してIEnumerable
(および新しいGetEnumerator
メソッドが返す列挙子クラスを作成することにより)クラスをステートメントで使用できるようにすることができます。
bool MoveNext()
メソッドとCurrent
プロパティを持つ型のインスタンスを返すGetEnumeratorメソッドだけです。この「ダックタイピング」アプローチは、値型コレクションを列挙するときにボックス化を回避する方法としてC#1.0で実装されました。
iterator
ですか?
IEnumerableおよびIEnumeratorインターフェイス
既存の.NETインターフェイスを実装するプロセスの調査を開始するために、まずIEnumerableとIEnumeratorの役割を見てみましょう。C#がforeachという名前のキーワードをサポートしていることを思い出してください。これにより、任意の配列型のコンテンツを反復処理できます。
// Iterate over an array of items.
int[] myArrayOfInts = {10, 20, 30, 40};
foreach(int i in myArrayOfInts)
{
Console.WriteLine(i);
}
配列型だけがこの構造を利用できるように見えるかもしれませんが、問題の真実は、GetEnumerator()という名前のメソッドをサポートするすべての型がforeach構造によって評価できることです。説明のために、私に従ってください!
Garageクラスがあるとします。
// Garage contains a set of Car objects.
public class Garage
{
private Car[] carArray = new Car[4];
// Fill with some Car objects upon startup.
public Garage()
{
carArray[0] = new Car("Rusty", 30);
carArray[1] = new Car("Clunker", 55);
carArray[2] = new Car("Zippy", 30);
carArray[3] = new Car("Fred", 30);
}
}
理想的には、データ値の配列のように、foreach構文を使用してGarageオブジェクトのサブアイテムを反復処理すると便利です。
// This seems reasonable ...
public class Program
{
static void Main(string[] args)
{
Console.WriteLine("***** Fun with IEnumerable / IEnumerator *****\n");
Garage carLot = new Garage();
// Hand over each car in the collection?
foreach (Car c in carLot)
{
Console.WriteLine("{0} is going {1} MPH",
c.PetName, c.CurrentSpeed);
}
Console.ReadLine();
}
}
悲しいことに、コンパイラは、GarageクラスがGetEnumerator()という名前のメソッドを実装していないことを通知します。このメソッドは、System.Collections名前空間内に潜むIEnumerableインターフェイスによって形式化されます。この動作をサポートするクラスまたは構造は、含まれているサブアイテムを呼び出し元に公開できることをアドバタイズします(この例では、foreachキーワード自体)。この標準.NETインターフェースの定義は次のとおりです。
// This interface informs the caller
// that the object's subitems can be enumerated.
public interface IEnumerable
{
IEnumerator GetEnumerator();
}
ご覧のとおり、GetEnumerator()メソッドは、System.Collections.IEnumeratorという名前のさらに別のインターフェイスへの参照を返します。このインターフェイスは、呼び出し元がIEnumerable互換コンテナーに含まれる内部オブジェクトをトラバースできるようにするインフラストラクチャを提供します。
// This interface allows the caller to
// obtain a container's subitems.
public interface IEnumerator
{
bool MoveNext (); // Advance the internal position of the cursor.
object Current { get;} // Get the current item (read-only property).
void Reset (); // Reset the cursor before the first member.
}
これらのインターフェースをサポートするようにガレージタイプを更新する場合は、長い道のりを取り、各メソッドを手動で実装できます。GetEnumerator()、MoveNext()、Current、Reset()のカスタマイズバージョンを自由に提供できますが、もっと簡単な方法があります。System.Array型(および他の多くのコレクションクラス)は既にIEnumerableとIEnumeratorを実装しているため、次のように単純に要求をSystem.Arrayに委任できます。
using System.Collections;
...
public class Garage : IEnumerable
{
// System.Array already implements IEnumerator!
private Car[] carArray = new Car[4];
public Garage()
{
carArray[0] = new Car("FeeFee", 200);
carArray[1] = new Car("Clunker", 90);
carArray[2] = new Car("Zippy", 30);
carArray[3] = new Car("Fred", 30);
}
public IEnumerator GetEnumerator()
{
// Return the array object's IEnumerator.
return carArray.GetEnumerator();
}
}
Garageタイプを更新したら、C#のforeachコンストラクト内でタイプを安全に使用できます。さらに、GetEnumerator()メソッドがパブリックに定義されている場合、オブジェクトユーザーはIEnumeratorタイプと対話することもできます。
// Manually work with IEnumerator.
IEnumerator i = carLot.GetEnumerator();
i.MoveNext();
Car myCar = (Car)i.Current;
Console.WriteLine("{0} is going {1} MPH", myCar.PetName, myCar.CurrentSpeed);
ただし、IEnumerableの機能をオブジェクトレベルから非表示にしたい場合は、明示的なインターフェイス実装を利用するだけです。
IEnumerator IEnumerable.GetEnumerator()
{
// Return the array object's IEnumerator.
return carArray.GetEnumerator();
}
そうすることで、カジュアルオブジェクトのユーザーはGarageのGetEnumerator()メソッドを見つけることができなくなり、必要に応じてforeachコンストラクトがバックグラウンドでインターフェースを取得します。
Garage
を取得しcarArray
ないのかということです。そうすればGetEnumerator
、Array
すでに実装しているので、自分で実装する必要はありません。例foreach(Car c in carLot.getCars()) { ... }
IEnumerableを実装するということは、クラスがIEnumeratorオブジェクトを返すことを意味します。
public class People : IEnumerable
{
IEnumerator IEnumerable.GetEnumerator()
{
// return a PeopleEnumerator
}
}
IEnumeratorを実装するということは、クラスが反復のためのメソッドとプロパティを返すことを意味します。
public class PeopleEnumerator : IEnumerator
{
public void Reset()...
public bool MoveNext()...
public object Current...
}
それがとにかく違いです。
最初にコードなしの説明、次に後で追加します。
あなたが航空会社を経営しているとしましょう。そして、各飛行機であなたは飛行機を飛んでいる乗客についての情報を知りたいです。基本的に、飛行機を「横切る」ことができるようにしたいです。つまり、前部座席から出発して、飛行機の後方に向かって作業し、乗客に情報を尋ねます。たとえば、誰であるか、どこから来たのかなどです。飛行機はこれしかできません、 もしそれが:
なぜこれらの要件なのか?それがインターフェースに必要なものだからです。
これが情報の過多である場合、知っておく必要があるのは、飛行機の各乗客に最初から最後までいくつか質問できることです。
カウント可能とはどういう意味ですか?
航空会社が「カウント可能」である場合、これは飛行機に客室乗務員が存在しなければならず、その唯一の仕事はカウントすることであり、この客室乗務員は非常に具体的な方法でカウントする必要があることを意味します。
カウント手順
航空会社の船長は、すべての乗客について、調査またはカウントされた時点でのレポートを求めています。したがって、最初の席の人に話しかけた後、客室乗務員/カウンターは機長に報告し、報告が与えられると、カウンターは通路内の正確な位置を記憶し、彼/彼女が去った場所で右カウントを続けますオフ。
このようにして、船長は常に調査中の現在の人物に関する情報を持つことができます。そうすれば、この個人がマンチェスターシティが好きだとわかった場合、その乗客に優遇措置を与えることができます。
これをIEnumerablesと関連付けましょう
列挙可能とは、飛行機の乗客の集まりです。民間航空法-これらは基本的にすべてのIEnumerableが従わなければならない規則です。航空会社のアテンダントが乗客情報を携えて船長に行くたびに、私たちは基本的に乗客を船長に「譲ります」。機長は基本的に、飛行機で乗客を並べ替えることを除いて、彼が乗客とやりたいことは何でもできる。この場合、マンチェスターシティをフォローすると、優遇措置が与えられます(ハァッ!)
foreach (Passenger passenger in Plane)
// the airline hostess is now at the front of the plane
// and slowly making her way towards the back
// when she get to a particular passenger she gets some information
// about the passenger and then immediately heads to the cabin
// to let the captain decide what to do with it
{ // <---------- Note the curly bracket that is here.
// we are now cockpit of the plane with the captain.
// the captain wants to give the passenger free
// champaign if they support manchester city
if (passenger.supports_mancestercity())
{
passenger.getFreeChampaign();
} else
{
// you get nothing! GOOD DAY SIR!
}
} // <---- Note the curly bracket that is here!
the hostess has delivered the information
to the captain and goes to the next person
on the plane (if she has not reached the
end of the plane)
概要
つまり、カウンターがあればカウント可能です。そしてカウンターは(基本的に):(i)その場所(状態)を覚えている、(ii)次に移動できる、(iii)彼が扱っている現在の人について知っている。
Enumerableは、「カウント可能」の単なる空想的な言葉です。言い換えると、列挙型を使用すると、「列挙」(つまり、カウント)できます。
IEnumerableはGetEnumeratorを実装します。呼び出されると、そのメソッドはMoveNext、Reset、およびCurrentを実装するIEnumeratorを返します。
したがって、クラスがIEnumerableを実装する場合、メソッド(GetEnumerator)を呼び出して、foreachなどのループで使用できる新しいオブジェクト(IEnumerator)を返すことができるということです。
IEnumerableを実装すると、リストのIEnumeratorを取得できます。
IEnumeratorは、yieldキーワードを使用して、リスト内の項目にforeachスタイルの順次アクセスを許可します。
foreach実装(Java 1.4など)の前に、リストを反復する方法は、リストから列挙子を取得し、次の値が返される限り、リストの「次の」項目を要求することでした。アイテムはnullではありません。Foreachは、lock()が舞台裏でMonitorクラスを実装するのと同じ方法で、それを暗黙的に言語機能として実行します。
IEnumerableを実装しているため、リストでforeachが機能することを期待しています。
リスト、スタック、ツリーのような列挙可能なオブジェクトについて考えてください。
IEnumerableとIEnumerator(およびそれらに対応する一般的なIEnumerable <T>とIEnumerator <T>)は、.Net Framework Class Librayコレクションの反復子実装の基本インターフェイスです。
IEnumerableは、世の中にあるコードの大部分で見られる最も一般的なインターフェースです。これは、foreachループ、ジェネレーター(yieldと考えます)を有効にし、その小さなインターフェースのために、厳密な抽象化を作成するために使用されます。IEnumerableはIEnumeratorに依存します。
一方、IEnumeratorは、わずかに低いレベルの反復インターフェースを提供します。これは明示的なイテレータと呼ばれ、反復サイクルをプログラマがより細かく制御できるようにします。
IEnumerableは、それをサポートするコレクションの反復を可能にする標準インターフェイスです(実際、今日考えることができるすべてのコレクション型は、IEnumerableを実装しています)。コンパイラのサポートにより、などの言語機能が可能になりますforeach
。一般的には、この暗黙の反復子の実装が可能になります。
foreach (var value in list)
Console.WriteLine(value);
foreach
ループはIEnumerableインターフェイスを使用する主な理由の1つだと思います。さまざまな変数をチェックして何をしているのかを確認する必要があるforeach
従来のCスタイルのループと比較して、構文は非常に簡潔で非常に理解しやすいです。
おそらくあまり知られていない機能は、IEnumerableがand ステートメントを使用してC#のジェネレーターも有効にすることです。yield return
yield break
IEnumerable<Thing> GetThings() {
if (isNotReady) yield break;
while (thereIsMore)
yield return GetOneMoreThing();
}
実際のもう1つの一般的なシナリオは、IEnumerableを使用して最小限の抽象化を提供することです。これは非常に小さく読み取り専用のインターフェイスであるため、コレクションをIEnumerableとして公開することをお勧めします(たとえば、リストではありません)。そうすれば、クライアントのコードを壊すことなく、実装を自由に変更できます(たとえば、リストをLinkedListに変更します)。
注意すべき動作の1つは、ストリーミングの実装(たとえば、最初にすべての結果をメモリにロードするのではなく、データベースから行ごとにデータを取得する)では、コレクションを2回以上繰り返すことができないことです。これは、問題なく複数回反復できるListなどのメモリ内コレクションとは対照的です。たとえば、ReSharperにはIEnumerableの可能な複数の列挙のコードインスペクションがあります。
一方、IEnumeratorは、IEnumerble-foreach-magicを機能させる舞台裏のインターフェースです。厳密に言えば、明示的なイテレータを使用できます。
var iter = list.GetEnumerator();
while (iter.MoveNext())
Console.WriteLine(iter.Current);
私の経験では、IEnumeratorはより冗長な構文とわずかに混乱するセマンティクスのため、一般的なシナリオではめったに使用されません(少なくとも私にとっては、たとえばMoveNext()も値を返しますが、名前はまったく示唆していません)。
私は使用のIEnumeratorを私が提供された特定の(わずかに低いレベル)のライブラリーとフレームワーク内のIEnumerableインターフェイスを。一例は、データストリーム処理ライブラリで、foreach
さまざまなファイルストリームとシリアル化を使用してデータが収集されたにもかかわらず、ループ内で一連のオブジェクトを提供していました。
クライアントコード
foreach(var item in feed.GetItems())
Console.WriteLine(item);
図書館
IEnumerable GetItems() {
return new FeedIterator(_fileNames)
}
class FeedIterator: IEnumerable {
IEnumerator GetEnumerator() {
return new FeedExplicitIterator(_stream);
}
}
class FeedExplicitIterator: IEnumerator {
DataItem _current;
bool MoveNext() {
_current = ReadMoreFromStream();
return _current != null;
}
DataItem Current() {
return _current;
}
}
IEnumerableとIEnumeratorの違い:
IEnumerableコレクションを別の関数に渡すと、アイテム/オブジェクトの現在の位置がわかりません(実行中のアイテムがわかりません)。
IEnumerableにはGetEnumerator()メソッドが1つあります
public interface IEnumerable<out T> : IEnumerable { IEnumerator<T> GetEnumerator(); }
IEnumeratorには、Currentと呼ばれる1つのプロパティと、Reset()とMoveNext()という2つのメソッドがあります(リスト内のアイテムの現在の位置を知るのに役立ちます)。
public interface IEnumerator
{
object Current { get; }
bool MoveNext();
void Reset();
}
IEnumerableは、Ienumeratorを含むボックスです。IEnumerableは、すべてのコレクションの基本インターフェイスです。コレクションがIEnumerableを実装している場合、foreachループは機能します。以下のコードでは、独自の列挙子を持つステップを説明しています。まず、コレクションを作成するクラスを定義しましょう。
public class Customer
{
public String Name { get; set; }
public String City { get; set; }
public long Mobile { get; set; }
public double Amount { get; set; }
}
次に、Customerクラスのコレクションとして機能するClassを定義します。IEnumerableインターフェースを実装していることに注意してください。したがって、メソッドGetEnumeratorを実装する必要があります。これにより、カスタムの列挙子が返されます。
public class CustomerList : IEnumerable
{
Customer[] customers = new Customer[4];
public CustomerList()
{
customers[0] = new Customer { Name = "Bijay Thapa", City = "LA", Mobile = 9841639665, Amount = 89.45 };
customers[1] = new Customer { Name = "Jack", City = "NYC", Mobile = 9175869002, Amount = 426.00 };
customers[2] = new Customer { Name = "Anil min", City = "Kathmandu", Mobile = 9173694005, Amount = 5896.20 };
customers[3] = new Customer { Name = "Jim sin", City = "Delhi", Mobile = 64214556002, Amount = 596.20 };
}
public int Count()
{
return customers.Count();
}
public Customer this[int index]
{
get
{
return customers[index];
}
}
public IEnumerator GetEnumerator()
{
return customers.GetEnumerator(); // we can do this but we are going to make our own Enumerator
return new CustomerEnumerator(this);
}
}
次のように、独自のカスタム列挙子を作成します。したがって、MoveNextメソッドを実装する必要があります。
public class CustomerEnumerator : IEnumerator
{
CustomerList coll;
Customer CurrentCustomer;
int currentIndex;
public CustomerEnumerator(CustomerList customerList)
{
coll = customerList;
currentIndex = -1;
}
public object Current => CurrentCustomer;
public bool MoveNext()
{
if ((currentIndex++) >= coll.Count() - 1)
return false;
else
CurrentCustomer = coll[currentIndex];
return true;
}
public void Reset()
{
// we dont have to implement this method.
}
}
これで、以下のようにコレクションに対してforeachループを使用できます。
class EnumeratorExample
{
static void Main(String[] args)
{
CustomerList custList = new CustomerList();
foreach (Customer cust in custList)
{
Console.WriteLine("Customer Name:"+cust.Name + " City Name:" + cust.City + " Mobile Number:" + cust.Amount);
}
Console.Read();
}
}
イテレーターパターンを理解しておくと役に立ちます。同じものを読むことをお勧めします。
高レベルでは、反復子パターンを使用して、任意のタイプのコレクションを反復する標準的な方法を提供できます。イテレーターパターンには、実際のコレクション(クライアント)、アグリゲーター、イテレーターの3人の参加者がいます。集約は、イテレータを返すメソッドを持つインターフェース/抽象クラスです。イテレーターは、コレクションを反復処理できるメソッドを持つインターフェース/抽象クラスです。
パターンを実装するには、まず、関係するコレクション(クライアント)を反復処理できる具象を生成するイテレーターを実装する必要があります。次に、コレクション(クライアント)がアグリゲーターを実装して、上記のイテレーターのインスタンスを返します。
したがって、基本的にc#では、IEnumerableは抽象集計であり、IEnumeratorは抽象Iteratorです。IEnumerableには、目的のタイプのIEnumeratorのインスタンスを作成する単一のメソッドGetEnumeratorがあります。リストのようなコレクションはIEnumerableを実装します。
例。我々は方法があるとしますgetPermutations(inputString)
文字列のリターンすべての順列ことやメソッドがのインスタンスを返すことIEnumerable<string>
順列の数を数えるために、以下のようなことができます。
int count = 0;
var permutations = perm.getPermutations(inputString);
foreach (string permutation in permutations)
{
count++;
}
c#コンパイラは、多かれ少なかれ、上記を
using (var permutationIterator = perm.getPermutations(input).GetEnumerator())
{
while (permutationIterator.MoveNext())
{
count++;
}
}
ご不明な点がございましたら、お気軽にお問い合わせください。
マイナーな貢献。
彼らの多くが「いつ使うか」と「foreachで使う」について説明しているように。IEnumerableとIEnumeratorの違いについて質問されたとおり、ここにAnother States Differenceを追加することを考えました。
以下のディスカッションスレッドに基づいて、以下のコードサンプルを作成しました。
IEnumerable、IEnumeratorとforeach、 IEnumeratorとIEnumerableの違いは何ですか?
Enumeratorは関数呼び出し間の状態(反復位置)を保持しますが、Enumerableは反復を保持しません。
これは、理解すべきコメント付きのテスト済みの例です。
専門家が私を追加/修正してください。
static void EnumerableVsEnumeratorStateTest()
{
IList<int> numList = new List<int>();
numList.Add(1);
numList.Add(2);
numList.Add(3);
numList.Add(4);
numList.Add(5);
numList.Add(6);
Console.WriteLine("Using Enumerator - Remembers the state");
IterateFrom1to3(numList.GetEnumerator());
Console.WriteLine("Using Enumerable - Does not Remembers the state");
IterateFrom1to3Eb(numList);
Console.WriteLine("Using Enumerable - 2nd functions start from the item 1 in the collection");
}
static void IterateFrom1to3(IEnumerator<int> numColl)
{
while (numColl.MoveNext())
{
Console.WriteLine(numColl.Current.ToString());
if (numColl.Current > 3)
{
// This method called 3 times for 3 items (4,5,6) in the collection.
// It remembers the state and displays the continued values.
IterateFrom3to6(numColl);
}
}
}
static void IterateFrom3to6(IEnumerator<int> numColl)
{
while (numColl.MoveNext())
{
Console.WriteLine(numColl.Current.ToString());
}
}
static void IterateFrom1to3Eb(IEnumerable<int> numColl)
{
foreach (int num in numColl)
{
Console.WriteLine(num.ToString());
if (num>= 5)
{
// The below method invokes for the last 2 items.
//Since it doesnot persists the state it will displays entire collection 2 times.
IterateFrom3to6Eb(numColl);
}
}
}
static void IterateFrom3to6Eb(IEnumerable<int> numColl)
{
Console.WriteLine();
foreach (int num in numColl)
{
Console.WriteLine(num.ToString());
}
}
私はこれらの違いに気づきました:
A.別の方法でリストを反復処理します。foreachはIEnumerableに使用でき、whileループはIEnumeratorに使用できます。
B. IEnumeratorは、あるメソッドから別のメソッドに渡したときに現在のインデックスを記憶できます(現在のインデックスで作業を開始します)が、IEnumerableはインデックスを記憶できず、インデックスを最初にリセットします。このビデオの詳細https://www.youtube.com/watch?v=jd3yUjGc9M0
IEnumerable
そして、IEnumerator
の両方がC#のインターフェイスです。
IEnumerable
インターフェースGetEnumerator()
を返す単一のメソッドを定義するIEnumerator
インターフェースです。
これIEnumerable
は、foreach
ステートメントで使用できるを実装するコレクションへの読み取り専用アクセスで機能します。
IEnumerator
には2つのメソッドがMoveNext
ありReset
ます。また、というプロパティもありますCurrent
。
以下は、IEnumerableとIEnumeratorの実装を示しています。
using System;
using System.Collections;
using System.Collections.Generic;
using System.Linq;
using System.Text;
using System.Threading.Tasks;
namespace Enudemo
{
class Person
{
string name = "";
int roll;
public Person(string name, int roll)
{
this.name = name;
this.roll = roll;
}
public override string ToString()
{
return string.Format("Name : " + name + "\t Roll : " + roll);
}
}
class Demo : IEnumerable
{
ArrayList list1 = new ArrayList();
public Demo()
{
list1.Add(new Person("Shahriar", 332));
list1.Add(new Person("Sujon", 333));
list1.Add(new Person("Sumona", 334));
list1.Add(new Person("Shakil", 335));
list1.Add(new Person("Shruti", 336));
}
IEnumerator IEnumerable.GetEnumerator()
{
return list1.GetEnumerator();
}
}
class Program
{
static void Main(string[] args)
{
Demo d = new Demo(); // Notice here. it is simple object but for
//IEnumerator you can get the collection data
foreach (Person X in d)
{
Console.WriteLine(X);
}
Console.ReadKey();
}
}
}
/*
Output :
Name : Shahriar Roll : 332
Name : Sujon Roll : 333
Name : Sumona Roll : 334
Name : Shakil Roll : 335
Name : Shruti Roll : 336
*/