17

以下のコードには、不変の構造体内に単純なLINQクエリが含まれています。

struct Point
{
   static readonly List</*enum*/> NeighborIndexes;
   //and other readonly fields!

    public IEnumerable<FlatRhombPoint> GetEdges()
    {
        return from neighborIndex in NeighborIndexes;
             select GetEdge(neighborIndex);
    }
}

コンパイルされません。

構造体内の匿名メソッド、ラムダ式、およびクエリ式は、「this」のインスタンスメンバーにアクセスできません。'this'を無名メソッド、ラムダ式、またはクエリ式の外部のローカル変数にコピーし、代わりにローカルを使用することを検討してください。

なぜこれが許可されないのか誰かが知っていますか?

メッセージが示唆する修正は正常に機能します:

    public IEnumerable<FlatRhombPoint> GetEdges()
    {
        var thisCopy = this;

        return from neighborIndex in NeighborIndexes;
             select thisCopy.GetEdge(neighborIndex);
    }

しかし、これは標準的な方法ですか?構造体にこのようなクエリがない理由はありますか?(より大きなスキームでは、コピーを作成しても、パフォーマンスの面で心配する必要はありません)。

4

1 に答える 1

21

構造体のインスタンスメソッドは、 –非表示パラメータへの参照を使用して呼び出されます。 これが、structメソッドが呼び出された構造体を変更できる理由です。thisref

thisラムダ式またはLINQクエリ内で(または他のローカル変数/パラメーター)を使用すると、コンパイラーはそれをコンパイラー生成クロージャクラスのフィールドに変換します

refCLRはフィールドをサポートしていないため、キャプチャさthisれたものが通常のと同じように機能することは不可能ですthisref(これは、ラムダ内でパラメーターを使用できない理由でもあります)

イテレータメソッドにも同じ問題があります。それらは非表示の列挙子クラスにコンパイルされ、すべての変数またはパラメータがクラスのフィールドになります(これがイテレータがrefパラメータを取得できない理由です)。
ただし、イテレータの場合、C#は反対の決定を行いました。イテレータ内では、を使用できますがthis、列挙子クラスのフィールドにコピーされます。
これは、イテレータ内で構造体を変更した場合、変更は呼び出し元のコピーには発生しないことを意味します。

于 2013-03-25T16:05:51.967 に答える