IEnumerable
を実装していない次のクラスがありますが、foreach
で完全に動作しています。また、配列はIEnumerable
を実装しなくても機能します。
では、なぜIEnumerable
をforeach
で使用するには、コレクションに実装する必要があると言っているのですか。混乱しています。助けてください。
class Parent
{
public int MyProperty { get; set; }
public void GetData()
{
Console.WriteLine("parent");
}
}
Parent[] p = new Parent[3]
{
new Parent(){MyProperty=90},
new Parent(){MyProperty=50},
new Parent(){MyProperty=100}
};
foreach (var item in p)
{
Parent it = item;
Console.WriteLine(it.MyProperty);
}
公式ドキュメント は私には簡単に見えます:
foreach
ステートメントは、_System.Collections.IEnumerable
_または_System.Collections.Generic.IEnumerable<T>
_インターフェイスを実装する配列またはオブジェクトコレクションの各要素に対して、埋め込みステートメントのグループを繰り返します。
C-#言語仕様に裏打ちされた kai's answer は、ドキュメントの制限が厳しすぎることを示しています。foreach
を持つクラスを使用するために、これらのインターフェイスの1つを実装する必要はありません。 。これらはそれ自体興味深い実装の詳細ですが、サンプルコードでは実際にIEnumerable
を使用しているため、元の質問には関係ありません。
何が起こるかというと、あなたが使用するシーケンスは配列であり、 ドキュメントによると 、私のものを強調します:
配列型は、抽象基本型
Array
から派生した参照型です。 この型はIEnumerable
および_IEnumerable<T>
_を実装しているため、C#のすべての配列でforeach
反復を使用できます。
同様に、 Array
クラスの定義 を確認できます。
_public abstract class Array : ICloneable, IList, ICollection,
IEnumerable, IStructuralComparable, IStructuralEquatable
_
インターフェースのIEnumerable
をご覧ください。
さらに興味深いことに、typeof(int[])
の結果を調べると、ImplementedInterfaces
プロパティのリストが表示されます。
typeof(ICloneable)
typeof(IList)
typeof(ICollection)
typeof(IEnumerable)
typeof(IStructuralComparable)
typeof(IStructuralEquatable)
typeof(IList<Int32>)
typeof(ICollection<Int32>)
typeof(IEnumerable<Int32>)
typeof(IReadOnlyList<Int32>)
typeof(IReadOnlyCollection<Int32>)
追加の汎用インターフェースも表示されます。 (これは、なぜ_IList<int> a = new[] { 1, 2, 3 };
_と書けるかを説明しています。)
同様に、 _List<T>
_ 、 _ConcurrentQueue<T>
_ などは、IEnumerable
および_Enumerable<T>
_を実装します。
だからあなたがそれを言うとき:
また、配列はIEnumerableを実装しなくても機能します。
これは単に真実ではありません。配列はIEnumerable
を実装しています。はどうかと言うと:
IEnumerableを実装していませんが、foreachと完全に連携している次のクラスがあります。
実際のコードを見ることは興味深いでしょう(質問にあるコードでは配列を使用しています)。おそらく何が起こっているのでしょうか?あなたのクラスは、そのインターフェースの中でIEnumerable
または_IEnumerable<T>
_を明示的に宣言せずに、 _ICollection<T>
_ などのより具体的なインターフェースを実装します。次に、IEnumerable
から継承します:
_public interface ICollection<T> : IEnumerable<T>, IEnumerable
_
そうでなければ、まあ、あなたはおそらくkaiの答えに示されているケースにいるでしょう。
必須ではありません。 foreach
キーワードは基本的にコードを「リライト」するだけなので、MoveNext()
を呼び出してCurrent
を参照するwhileループになりますが、インターフェースIEnumerable
はt必要。これは、セクション_15.8.4 The foreach statement
_の C#言語仕様 によってバックアップされます。
次のコードをコンパイルしてみてください。msbuildには何も言うことがありません。
_public class Foo<T>
{
public Foo(T value)
{
Value = value;
}
public T Value { get; }
public FooEnumerator<T> GetEnumerator()
{
return new FooEnumerator<T>(Value);
}
}
public class FooEnumerator<T>
{
private bool _hasValue = true;
private readonly T _value;
public FooEnumerator(T value)
{
_value = value;
}
public bool MoveNext()
{
if (_hasValue)
{
Current = _value;
return true;
}
else
{
_hasValue = false;
return _hasValue;
}
}
public T Current { get; private set; }
}
public class Test
{
public static void Run()
{
foreach (var x in new Foo<int>(1))
{
Console.WriteLine(x);
}
}
}
_
foreach
キーワードを使用するために必要なのは、GetEnumerator()
というメソッドがbool
というメソッドを返すMoveNext()
というメソッドがあることだけです。 ] _およびCurrent
と呼ばれる(任意のタイプの)プロパティで、少なくとも可視のゲッター
LINQクエリ内包と同様に機能します。次のコードは、コンパイルしてスムーズに実行します。
_public class Foo<T>
{
public Foo(T value)
{
Value = value;
}
public T Value { get; }
public Foo<U> Select<U>(Func<T, U> f)
{
return new Foo<U>(f(Value));
}
public Foo<U> SelectMany<U>(Func<T, Foo<U>> f)
{
return f(Value);
}
public Foo<V> SelectMany<U, V>(Func<T, Foo<U>> f, Func<T, U, V> g)
{
return new Foo<V>(g(Value, f(Value).Value));
}
}
public class Test
{
public static void Run()
{
var result =
from x in new Foo<int>(5)
from y in new Foo<int>(9)
select (x + y).ToString();
// result is now a Foo<string> containing the value "14"
}
}
_
エリックリッパートは実際にこれについて一片を持っています:
https://blogs.msdn.Microsoft.com/ericlippert/2011/06/30/following-the-pattern/
エリックの説明:
コレクションのタイプにはGetEnumeratorというパブリックメソッドが必要であり、Currentと呼ばれるパブリックプロパティゲッターとboolを返すパブリックメソッドMoveNextを持つタイプを返す必要があります。
興味深いことに(そしてこの記事で言及しているように)、これはC#1.0のジェネリックの欠如の遺物です。 C#コンパイラは、ダックタイピングを使用して、列挙型がObject
を返すのではなく、コレクションに適切なタイプを返し、値タイプのボックス化/ボックス化解除のコストを発生させました。
私がまだ大学にいたときにこれについて学び、C#を学んだことを覚えています。そのとき、私は気分が悪くなりました。今、私はそれが一種の賢いことを見つけました!