20

リファクタリング セーフな方法で実行時にローカル変数 (およびパラメーター) の名前を取得することに興味があります。次の拡張メソッドがあります。

public static string GetVariableName<T>(Expression<Func<T>> variableAccessExpression)
{
    var memberExpression = variableAccessExpression.Body as MemberExpression;
    return memberExpression.Member.Name;
}

…これは、ラムダ式を通じてキャプチャされた変数の名前を返します。

static void Main(string[] args)
{
    Console.WriteLine(GetVariableName(() => args));
    // Output: "args"

    int num = 0;
    Console.WriteLine(GetVariableName(() => num));
    // Output: "num"
}

ただし、これが機能するのは、C# コンパイラが匿名関数でキャプチャされたローカル変数 (およびパラメーター) を、舞台裏でコンパイラによって生成されたクラス内の同じ名前のインスタンス変数に昇格させるためです ( Jon Skeetによる)。そうでない場合、BodytoのキャストMemberExpressionは失敗します。これは、MemberExpressionフィールドまたはプロパティへのアクセスを表すためです。

この変数の昇格は文書化された動作ですか、それとも実装の詳細はフレームワークの他のバージョンで変更される可能性がありますか?

注:この質問は、引数の検証に関する以前の質問の一般化です。

4

3 に答える 3

12

更新: これは、C# 6 からの問題ではなくなりました。C# 6 では、このnameofようなシナリオに対処するためにオペレーターが導入されました ( MSDNを参照)。

私の質問に対する答えはノーのようです。この機能は標準化されていません。状況は、私が当初考えていたよりもさらに厳しいようです。キャプチャされた変数の昇格が標準化されていないだけでなく、無名関数を式ツリー表現に変換する仕様全体も標準化されていません。

これが意味することは、以下のような単純な無名関数でさえ、フレームワークのさまざまな実装間で一貫した式ツリーになることが保証されていないということです (変換が標準化されるまで)。

Expression<Func<int, int, int>> add = (int x, int y) => x + y;

次の抜粋は、C# 言語仕様 4.0からの抜粋です(すべての場合に強調が追加されています)。

「4.6 式ツリーの種類」より:

ジェネリック型の正確な定義とExpression<D>、無名関数が式ツリー型に変換されるときに式ツリーを構築するための正確な規則は、どちらもこの仕様の範囲外であり、他の場所で説明されています。

「6.5.2 無名関数の式木型への変換評価」より:

無名関数を式ツリー型に変換すると、式ツリーが生成されます (§4.6)。より正確には、無名関数変換の評価は、無名関数自体の構造を表すオブジェクト構造の構築につながります。式ツリーの正確な構造と、それを作成するための正確なプロセスは、実装によって定義されます。

「6.5.3 実装例」の 3 番目の例は、ローカル変数をキャプチャする無名関数の変換を示し、私の質問で言及されている変数の昇格を確認します。

ローカル変数の有効期間は、少なくとも無名関数デリゲートの有効期間まで延長する必要があります。これは、コンパイラが生成したクラスのフィールドにローカル変数を「ホイスト」することで実現できます。ローカル変数のインスタンス化 (§7.15.5.2) は、コンパイラが生成したクラスのインスタンスを作成することに対応し、ローカル変数にアクセスすることは、コンパイラが生成したクラスのインスタンス内のフィールドにアクセスすることに対応します

これは、セクションの最後でさらに裏付けられています。

ローカル変数をキャプチャするためにここで適用したのと同じ手法を、無名関数を式ツリーに変換するときにも使用できます。コンパイラによって生成されたオブジェクトへの参照は式ツリーに格納でき、ローカル変数へのアクセスはこれらのオブジェクトのフィールド アクセスとして表すことができます。 . このアプローチの利点は、「リフトされた」ローカル変数をデリゲートと式ツリーの間で共有できることです。

ただし、セクションの冒頭に免責事項があります。

ここで説明する実装は、Microsoft C# コンパイラで使用されているのと同じ原則に基づいていますが、必須の実装ではなく、可能な唯一の実装でもありません。正確なセマンティクスはこの仕様の範囲外であるため、式ツリーへの変換については簡単に説明するだけです。

PS Eric Lippertは、このコメントで、式ツリーの仕様が出荷されなかったことを確認しています。CodePlex の DLR ドキュメントの下にExpression Trees v2 Specが存在しますが、その範囲は、C# での無名関数の式ツリーへの変換をカバーしていないようです。

于 2012-06-17T12:25:37.963 に答える
4

これは、依存してはならない動作です。

Abuse of C# ラムダ式または Syntax brilliance をご覧ください

C# 設計チームの Eric Lippert のコメントを読んでください。それらには以下が含まれます:

Anders (およびその他のデザイン チーム) にどう思うか聞いてみました。結果は家族向けの新聞に印刷できないとだけ言っておきましょう

なぜこれが恐ろしいのかというと、ラムダの設計者が想定している遅い、もろい、移植性がなく、不要

これらの声明から、それは文書化されたりサポートされたりすることはないと言えます。

于 2012-06-16T19:14:30.647 に答える
1

私の知る限り、それは実装の詳細です。

ただし、実際には変わらないことは間違いないと思います。

VS2012 RC でテストしたところ、期待どおりに動作するので、少なくとも 2 年間は安全です。

于 2012-06-16T13:15:00.570 に答える