21

私はいくつかのコードをより読みやすくしようとしています。たとえばforeach(var row in table) {...}、ではなくforeach(DataRow row in table.Rows) {...}

これを行うために、拡張メソッドを作成しました。

namespace System.Data {
    public static class MyExtensions {
        public static IEnumerable<DataRow> GetEnumerator( this DataTable tbl ) {
            foreach ( DataRow r in tbl.Rows ) yield return r;
        }
    }
}

しかし、コンパイラはそれでもをスローしforeach statement cannot operate on variables of type 'System.Data.DataTable' because 'System.Data.DataTable' does not contain a public definition for 'GetEnumerator'ます。

拡張メソッドを適切に実装したことを確認するために、代わりに次のコードを試しましたが、コンパイラーは問題ありませんでした。

for ( IEnumerator<DataRow> enm = data.GetEnumerator(); enm.MoveNext(); ) {
    var row = enm.Current;
    ...
}

IEnumerator実装されているか実装されていないためであると言う前にIEnumerator<DataRow>、以下がコンパイルされることを考慮してください。

public class test {
    public void testMethod() {
        foreach ( var i in new MyList( 1, 'a', this ) ) { }
    }
}
public class MyList {
    private object[] _list;
    public MyList( params object[] list ) { _list = list; }
    public IEnumerator<object> GetEnumerator() { foreach ( var o in _list ) yield return o; }
}
4

8 に答える 8

42

これまでのところ、他の回答には多くの混乱があります。(プレストンギロットの答えはかなり良いですが、実際にはここで何が起こっているのかを指で触れません。)明確にしようと思います。

まず第一に、あなたは単に運が悪いです。C#では、foreachステートメントで使用されるコレクションが次のいずれかである必要があります。

  1. GetEnumerator必要なパターンに一致するパブリックを実装します。
  2. 実装しますIEnumerable(そしてもちろん、IEnumerable<T>が必要ですIEnumerable
  3. 動的になります。この場合、缶を蹴り出し、実行時に分析を行います。

結果として、コレクションタイプは実際GetEnumeratorにいずれかの方法で実装する必要があります。拡張メソッドを提供しても、それを削減することはできません。

これは残念です。私の意見では、C#チームがC#3に拡張メソッドを追加したとき、拡張メソッドを検討するためにforeach(そしておそらくusing!)などの既存の機能を変更する必要がありました。ただし、C#3のリリースサイクル中のスケジュールは非常に厳しく、LINQが時間どおりに実装されなかった余分な作業項目は削減される可能性がありました。この点についてデザインチームが言ったことを正確に思い出せず、メモもありません。

この不幸な状況は、言語が成長し進化するという事実の結果です。古いバージョンは時代のニーズに合わせて設計されており、新しいバージョンはその基盤の上に構築する必要があります。逆に、C#1.0に拡張メソッドとジェネリックスがあった場合、foreachループはLINQ:のように単純な構文変換として設計できたはずです。しかし、そうではありませんでした。そして今、私たちは、プレジェネリック、プレエクステンションメソッドの設計の遺産にとらわれています。

第二に、他の回答やコメントには、仕事をするために正確に何が必要かについての誤った情報があるようですforeach。を実装する必要はありませんIEnumerable。このよく誤解されている機能の詳細については、このテーマに関する私の記事を参照してください。

第三に、この振る舞いが実際に仕様によって正当化されるかどうかについて、いくつかの疑問があるようです。です。仕様では、この場合、拡張メソッドが考慮されないことを明示的に指定していません。これは残念なことです。ただし、仕様は何が起こるかについて非常に明確です。

コンパイラは、のメンバールックアップを実行することから始めますGetEnumerator。メンバールックアップアルゴリズムはセクション7.3で詳細に文書化されており、メンバールックアップは拡張メソッドを考慮せず、実際のメンバーのみを考慮します。拡張メソッドは、通常のオーバーロード解決が失敗した後にのみ考慮され、オーバーロード解決にはまだ到達していません。(はい、拡張メソッドはメンバーアクセスによって考慮されますが、メンバーアクセスメンバールックアップは異なる操作です。)

メンバールックアップでメソッドグループが見つからない場合、パターンを一致させる試みは失敗します。したがって、コンパイラはアルゴリズムの過負荷解決部分に進むことはなく、したがって拡張メソッドを検討する機会はありません。

したがって、説明する動作は、指定された動作と一致しています。

コンパイラがステートメントを分析する方法を正確に理解したい場合は、仕様のセクション8.8.4を注意深く読むことをお勧めします。foreach

第4に、他の方法でプログラムに付加価値を与えることに時間を費やすことをお勧めします。の魅力的なメリット

foreach (var row in table)

以上

foreach(var row in table.Rows)

開発者にとっては小さく、顧客には見えません。すでに完全にクリアなコードを5文字短くするのではなく、新機能の追加、バグの修正、パフォーマンスの分析に時間を費やしてください。

于 2013-01-05T14:18:04.087 に答える
3

テストクラスのGetEnumeratorメソッドは静的ではなく、拡張メソッドは静的です。これもコンパイルされません:

class test
{
}

static class x
{
    public static IEnumerator<object> GetEnumerator(this test t) { return null; }
}

class Program
{
    static void Main(string[] args)
    {
        foreach (var i in new test()) {  }
    }
}

foreach構文のシュガーが機能するためには、クラスがパブリックGetEnumeratorインスタンスメソッドを公開する必要があります。

編集:

C#9.0以降、拡張メソッドにすることがGetEnumerator できます。

于 2013-01-05T03:17:06.427 に答える
0

foreachステートメント内で、コンパイラーはGetEnumeratorのインスタンスメソッドを探しています。したがって、タイプ(ここではDataTable)はIEnumerableを実装する必要があります。静的であるため、代わりに拡張メソッドが見つかることはありません。foreachに拡張メソッドの名前を書き込む必要があります。

namespace System.Data {
    public static class MyExtensions {
        public static IEnumerable<DataRow> GetEnumerator( this DataTable table ) {
            foreach ( DataRow r in table.Rows ) yield return r;
        }
    }
}

foreach(DataRow row in table.GetEnumerator())
  .....

混乱を避けるために、拡張メソッドには別の名前を使用することをお勧めします。たぶんGetRows()のようなもの

于 2013-01-05T14:55:51.230 に答える
0

いくつかのオフトピック:もっと読みやすい書き込みをしたい場合

foreach ( DataRow r in tbl.Rows ) yield return r;

なので

foreach (DataRow row in tbl.Rows) 
{
    yield return row;
}

今あなたの問題に..これを試してみてください

    public static IEnumerable<T> GetEnumerator<T>(this DataTable table)
    {
        return table.Rows.Cast<T>();
    }
于 2013-01-05T03:06:52.923 に答える
0

拡張機能は次のものと同等です。

    public static IEnumerable<TDataRow> GetEnumerator<TDataRow>( this DataTable tbl ) {
        foreach ( TDataRow r in tbl.Rows ) yield return r;
    }

GetEnumerator<TDataRow>と同じ方法ではありませんGetEnumerator

これはうまく機能します:

    public static IEnumerable<DataRow> GetEnumerator( this DataTable tbl ) {
        foreach (DataRow r in tbl.Rows ) yield return r;
    }
于 2013-01-05T03:36:35.763 に答える
0

拡張機能を介してGetEnumeratorをC#に追加する現在の提案https://github.com/dotnet/csharplang/issues/3194

于 2020-02-17T03:49:09.943 に答える
0

これはC#9で可能になります。プロポーザルは既にチェックインされています。プレビューバージョンで利用可能になる時期は、言語機能のステータス-C#9で確認できます。

提案の詳細設計セクションでは、次のように述べています。

それ以外の場合は、タイプ'X'に適切なGetEnumerator拡張メソッドがあるかどうかを判別します

using System;
using System.Collections.Generic;

public static class MyExtensions
{
    // Note: ranges aren't intended to work like this. It's just an example.
    public static IEnumerable<int> GetEnumerator(this Range range)
    {
        // .. do validation ..
        for (var i = range.Start.Value; i <= range.End.Value; i++)
        {
            yield return i;
        }
    }
}

public class ExtensionGetEnumerator
{

    public void Method()
    {
        var range = 1..2;
        foreach (var i in range.GetEnumerator())
        {
            Console.WriteLine($"Print with explicit GetEnumerator {i}");
        }

        // The feature is in progress, scheduled for C# 9, the below does not compile yet

        //foreach (var i in range)
        //{
        //    Console.WriteLine($"Print with implicit GetEnumerator {i}");
        //}
    }
}
于 2020-07-22T09:35:11.170 に答える
-6

foreachmust実装のオブジェクトコレクションSystem.Collections.IEnumerableまたはSystem.Collections.Generic.IEnumerable<T>

これを有効にしたいという強い願望がある場合は、自分IEnumerableへのポインタを実装して持つラッパークラスを作成できますDataTableDataTableまたは、新しいクラスを継承して実装することもできますIEnumerable

コードの可読性は、ほとんどの場合、個人的な好みです。私は個人的に、ステートメントに対するあなたの変更がforeach読みにくいと感じています(しかし、SOにはあなたに同意する人がたくさんいると確信しています)。

于 2013-01-05T04:35:34.480 に答える