誰でもIEnumerableとIEnumeratorを私に説明できますか?
たとえば、foreachでいつ使用するか? IEnumerableとIEnumeratorの違いは何ですか?なぜ使用する必要があるのですか?
たとえば、foreachでいつ使用するか?
IEnumerable
"over" foreach
は使用しません。 IEnumerable
を実装すると、foreach
possibleが使用されます。
次のようなコードを書くとき:
foreach (Foo bar in baz)
{
...
}
機能的には次の記述と同等です。
IEnumerator bat = baz.GetEnumerator();
while (bat.MoveNext())
{
bar = (Foo)bat.Current
...
}
「機能的に同等」とは、実際にコンパイラがコードを変換するものであることを意味します。この例のforeach
でbaz
を使用することはできませんnlessbaz
はIEnumerable
を実装します。
IEnumerable
は、baz
がメソッドを実装することを意味します
IEnumerator GetEnumerator()
このメソッドが返すIEnumerator
オブジェクトは、メソッドを実装する必要があります
bool MoveNext()
そして
Object Current()
最初のメソッドは、列挙子を作成したIEnumerable
オブジェクト内の次のオブジェクトに進み、完了した場合はfalse
を返し、2番目のメソッドは現在のオブジェクトを返します。
.Netで反復できるものはすべてIEnumerable
を実装します。独自のクラスを構築していて、IEnumerable
を実装するクラスからまだ継承していない場合は、foreach
を実装して(および新しいIEnumerable
メソッドが返す列挙子クラスを作成することで)GetEnumerator
ステートメントでクラスを使用可能にすることができます。
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 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コンストラクトは必要に応じてバックグラウンドでインターフェイスを取得します。
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...
}
とにかく違いです。
最初にコードなしで説明し、それから後で追加します。
航空会社を経営しているとしましょう。そして、各飛行機では、飛行機で飛んでいる乗客についての情報を知りたいです。基本的には、飛行機を「横断」できるようにしたいです。言い換えると、フロントシートから出発して、飛行機の後方に向かって進み、乗客に情報を尋ねます。彼らは誰なのか、どこから来たのか、などです。飛行機でしかできません。 、 もしそれが:
これらの要件はなぜですか?それがインターフェースに必要なものだからです。
これが情報過多の場合、知っておく必要があるのは、飛行機の各乗客に最初から始めて最後まで進むための質問をいくつかできるようにすることです。
countableはどういう意味ですか?
航空会社が「可算」である場合、これは飛行機に乗務員が存在しなければならないことを意味します。乗務員の唯一の仕事はカウントです。この乗務員は非常に具体的な方法でカウントする必要があります。
カウント手順
航空会社のキャプテンは、すべての乗客が調査またはカウントされた時点でのレポートを求めています。したがって、最初の席の人に話しかけた後、客室乗務員/カウンターは機長に報告し、報告が与えられると、カウンターは通路での彼/彼女の正確な位置を記憶し、彼/彼女が去った場所で右に数え続けますオフ。
このようにして、船長は常に調査対象の現在の人物に関する情報を持つことができます。そうすれば、この個人がマンチェスターシティが好きだとわかったら、その乗客に優遇措置などを与えることができます。
これをIEnumerablesと結びましょう
列挙型は、飛行機の乗客の集まりです。民間航空法-これらは基本的にすべてのIEnumerablesが従わなければならない規則です。航空会社のアテンダントが乗客情報をキャプテンに行くたびに、基本的に乗客をキャプテンに「譲歩」します。キャプテンは基本的に乗客と一緒に何でもできます-乗客を飛行機に再配置することを除いて。この場合、マンチェスターシティをフォローしている場合、彼らは優遇されます(ugh!)
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)その場所を記憶します(state)、(ii) next、(iii)に移動し、彼が扱っているcurrent人について知る。
Enumerableは、 "countable"の単なる高級な言葉です。つまり、enumerableを使用すると、「列挙」(つまり、カウント)できます。
IEnumerable GetEnumeratorを実装します。呼び出されると、そのメソッドは、MoveNext、Reset、およびCurrentを実装する IEnumerator を返します。
したがって、クラスがIEnumerableを実装するとき、メソッド(GetEnumerator)を呼び出して、foreachなどのループで使用できる新しいオブジェクト(IEnumerator)を取得できると言っています。
IEnumerableを実装すると、リストのIEnumeratorを取得できます。
IEnumeratorでは、yieldキーワードを使用して、リスト内のアイテムへのforeachスタイルの順次アクセスが可能です。
Foreach実装の前(たとえばJava 1.4)、リストを反復する方法は、リストから列挙子を取得し、リスト内の「次の」項目を要求することでした。次のアイテムとして返される値はnullではありません。 foreachは、lock()が舞台裏でMonitorクラスを実装するのと同じように、単に言語機能として暗黙的にそれを行います。
ForeachはIEnumerableを実装しているため、リストで機能することを期待しています。
リスト、スタック、ツリーのような列挙可能なオブジェクトを考えてください。
IEnumerableおよびIEnumerator(およびそれらに対応する汎用のIEnumerable <T>およびIEnumerator <T>)は、 。Iterator。Net Framework Class Libray collections の実装のベースインターフェイスです。
IEnumerableは、ほとんどのコードで見られる最も一般的なインターフェイスです。 foreachループ、ジェネレーター(yieldなど)を有効にし、その小さなインターフェイスのために、緊密な抽象化を作成するために使用されます。 IEnumerableはIEnumeratorに依存 。
一方、IEnumeratorは、やや低レベルの反復インターフェイスを提供します。これは explicit iterator と呼ばれ、プログラマーが反復サイクルをより細かく制御できるようにします。
IEnumerableは、それをサポートするコレクションの繰り返し処理を可能にする標準インターフェイスです(実際、今日考えられるすべてのコレクションタイプはIEnumerableを実装しています)。コンパイラーのサポートにより、foreach
などの言語機能が可能になります。一般的に、これはこれを有効にします 暗黙的なイテレータの実装 。
foreach (var value in list)
Console.WriteLine(value);
foreach
ループは、IEnumerableインターフェイスを使用する主な理由の1つだと思います。 foreach
の構文は非常に簡潔で、さまざまな変数をチェックして実行内容を確認する必要があるループの従来のCスタイルと比較すると、非常にわかりやすいです。
おそらくあまり知られていない機能は、IEnumerableもyield return
およびyield break
ステートメントを使用して C#のジェネレーター を有効にすることです。
IEnumerable<Thing> GetThings() {
if (isNotReady) yield break;
while (thereIsMore)
yield return GetOneMoreThing();
}
実際のもう1つの一般的なシナリオは、IEnumerableを使用して最小限の抽象化を提供することです。これは非常に小さく読み取り専用のインターフェースであるため、コレクションをIEnumerableとして公開することをお勧めします(たとえばListではなく) )。そうすれば、クライアントのコードを壊さずに実装を自由に変更できます(たとえば、ListをLinkedListに変更します)。
留意すべき動作の1つは、ストリーミング実装(たとえば、最初にすべての結果をメモリにロードする代わりにデータベースから行ごとにデータを取得する)で、コレクションに対してcannot反復することです一回以上。これは、Listのようなメモリ内コレクションとは対照的であり、問題なく複数回繰り返すことができます。 ReSharperは、たとえば、 IEnumerableの可能な複数の列挙のコード検査があります です。
一方、IEnumeratorは、IEnumerble-foreach-magicを機能させる舞台裏のインターフェイスです。厳密に言えば、明示的な反復子を有効にします。
var iter = list.GetEnumerator();
while (iter.MoveNext())
Console.WriteLine(iter.Current);
私の経験では、IEnumeratorは、より冗長な構文とわずかに混乱したセマンティクスのために、一般的なシナリオではめったに使用されません(少なくとも私にとっては、例えばMoveNext()も値を返しますが、名前ではまったく示唆されていません)。
私はIEnumeratorのみを使用しました(特に低レベルの)ライブラリとフレームワークIEnumerableインターフェイスを提供していました。 1つの例は、さまざまなファイルストリームとシリアル化を使用して背後でデータが収集されたにもかかわらず、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
およびIEnumerator
は、foreach
ループの一部として透過的に使用されます。
IEnumerableとIEnumeratorの違い:
IEnumerableコレクションを別の関数に渡すたびに、アイテム/オブジェクトの現在の位置がわかりません(どのアイテムが実行されているかわかりません)
IEnumerableには1つのメソッドGetEnumerator()があります
public interface IEnumerable<out T> : IEnumerable { IEnumerator<T> GetEnumerator(); }
IEnumeratorには、1つのプロパティcurrentと2つのメソッドResetおよびMoveNextがあります(リスト内のアイテムの現在の位置を知るのに役立ちます)。
public interface IEnumerator
{
object Current { get; }
bool MoveNext();
void Reset();
}
Iteratorパターンを理解しておくと役立ちます。同じものを読むことをお勧めします。
高レベルでは、反復子パターンを使用して、あらゆるタイプのコレクションを反復処理する標準的な方法を提供できます。イテレータパターンには、実際のコレクション(クライアント)、アグリゲータ、イテレータの3人の参加者がいます。集約は、イテレータを返すメソッドを持つインターフェース/抽象クラスです。 Iteratorは、コレクションを反復処理できるメソッドを持つインターフェイス/抽象クラスです。
パターンを実装するには、まずイテレータを実装して、関連するコレクション(クライアント)を反復処理できる具象を生成する必要があります。次に、コレクション(クライアント)はアグリゲータを実装して、上記のイテレータ。
したがって、基本的に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++;
}
}
ご不明な点がございましたら、お気軽にお問い合わせください。
IEnumerableは、Ienumeratorを含むボックスです。 IEnumerableは、すべてのコレクションの基本インターフェイスです。コレクションがIEnumerableを実装している場合、foreachループが動作します。以下のコードでは、独自の列挙子を持つステップを説明しています。コレクションを作成するClassを最初に定義します。
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();
}
}
マイナーな貢献。
それらの多くは、「いつ使用するか」と「foreachで使用する」について説明しています。ここで別のStates Differenceを、IEnumerableとIEnumeratorの両方の違いについての質問に応じて追加することを考えました。
以下のディスカッションスレッドに基づいて、以下のコードサンプルを作成しました。
IEnumerable、IEnumerator vs foreach、whatを使用するタイミング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=jd3yUjGc9M
IEnumerable
とIEnumerator
はどちらもC#のインターフェイスです。
IEnumerable
は、IEnumerator
インターフェイスを返す単一のメソッドGetEnumerator()
を定義するインターフェイスです。
これは、IEnumerable
をforeach
ステートメントで使用できることを実装するコレクションへの読み取り専用アクセスで機能します。
IEnumerator
には、MoveNext
とReset
の2つのメソッドがあります。また、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
*/