1

C# がアルファ変換をサポートしていないのはなぜですか?

int n = 3;
int[] numbers = { 5, 4, 1, 3, 9, 8, 6, 7, 2, 0 };
int oddNumbers = numbers.Count(n => n % 2 == 1);
Console.Out.WriteLine("N value = " + n);

収率:

「n」という名前のローカル変数をこのスコープで宣言することはできません。これは、「親または現在の」スコープで別のものを示すために既に使用されている「n」に別の意味を与えるためです。

非常にばかげているように聞こえるので、私が気づいていない粒子状の理由はありますか?

4

4 に答える 4

11

まず、奇妙さのテストが間違っています。

あなたの質問への答えは、あなたの質問の前提を否定することです。これはアルファ変換とは何の関係もありません。

また、「レキシカルスコープ」とは何の関係もありません.leppieは、私のレキシカルスコープの理解とは異なるものを意味しているようです。C# はレキシカル スコープの言語です。

ここで強調したいのは、C# で一方が他方を隠す 2 つの locals を宣言することは違法であることです。他のスコープに隠すことは完全に合法です。型パラメーターは外部の型パラメーターを隠すことができます (ただし、そうするのは非常にばかげています。そうしないでください。) フィールドは基本クラス フィールドを隠すことができます (ただし、その事実を強調するために、隠しフィールドを 'new' としてマークする必要があります)。 .) ローカルはメソッドを隠すことができます。等々。

ただし、(1) バグ ファームが作成され、(2) 単純な名前の使用に関するより一般的な規則に違反するため、ローカルは別のローカルを非表示にすることはできません。

名前に関するそのルールは、ここで興味深いルールです。これを行うと、同様のエラーが発生します。

class C
{
    int n;
    void M()
    {
        Console.WriteLine(n); // n means this.n
        Func<double, double> f = n=>n; // n means the formal parameter
    }
}

取得しているエラーは、単純な名前が最初に使用されるローカル スコープ全体で一貫した意味を持つ必要があるというC# の規則に違反しているためです。

「n」が 1 つの行で 1 つのことを意味し、次の行でまったく異なることを意味するプログラムは、混乱を招き、バグが発生しやすく、したがって違法です。

それを行いたい場合は、「n」の 2 つの意味が重複しないスコープ内にある必要があります。

class C
{
    int n;
    void M()
    {
        {
          Console.WriteLine(n); // n means this.n
        }
        Func<double, double> f = n=>n; // n means the formal parameter
    }
}

n の 2 つの使用法が重複しないスコープにあるため、これは合法です。

この問題はアルファ変換とは何の関係もありません。C# は、必要に応じてアルファ変換を適切に行います。

また、C# はレキシカル スコープであるため、この規則に違反しているとコンパイラが判断できます。これは、C# にレキシカル スコープがないという証拠ではありません。それは字句スコープを持っている証拠です

このルールの詳細については、この件に関する私の記事を参照してください。

http://blogs.msdn.com/b/ericlippert/archive/2009/11/02/simple-names-are-not-so-simple.aspx

于 2012-01-25T14:51:46.897 に答える
1

これは実際にはアルファ変換ではありません。

問題は、C# には適切なレキシカル スコープがないことです。これは、変数が内部スコープでシャドーイングされる可能性があることを意味します。

于 2012-01-25T10:43:38.427 に答える
0

これが発生する理由は、C# ラムダ関数がスコープで宣言されていないためです。親スコープ内の変数にアクセスできます。たとえば、次のコードは、ラムダが変数 n にアクセスできるようにします。

int outerN = 3; 
int[] numbers = { 5, 4, 1, 3, 9, 8, 6, 7, 2, 0 }; 
int oddNumbers = numbers.Count(localN => localN % 2 == outerN); 
Console.Out.WriteLine("N value = " + outerN); 

ラムダ関数内の変数は、宣言されているメソッドと同じスコープ内にあるものとして扱う必要があります。

MSDNから:

ラムダは、ラムダが定義されている外側のメソッドまたは型のスコープ内にある外部変数を参照できます。この方法でキャプチャされた変数は、変数がスコープ外になり、ガベージ コレクトされる場合でも、ラムダ式で使用するために格納されます。外部変数は、ラムダ式で使用する前に確実に割り当てる必要があります。

ラムダ式の変数スコープには、次の規則が適用されます。

  • キャプチャされた変数は、それを参照するデリゲートがスコープ外になるまでガベージ コレクションされません。

  • ラムダ式内で導入された変数は、外側のメソッドでは表示されません。

  • ラムダ式は、外側のメソッドから ref または out パラメーターを直接キャプチャすることはできません。

  • ラムダ式の return ステートメントは、外側のメソッドを返しません。

  • ラムダ式には、ターゲットが本体の外側または含まれる無名関数の本体内にある goto ステートメント、break ステートメント、または continue ステートメントを含めることはできません。

于 2012-01-25T10:47:03.480 に答える
0

適切な用語は「シャドウイング」です。「C# では、あるローカル変数が別のローカル変数をシャドウすることはできません」のように。

制限に技術的な理由はありません。シャドーイングは、型チェックやコード生成に問題を引き起こしません。

この制限の動機は、純粋に人間工学的なものだと思います。Sun の人々が Java を設計していたとき (注: Java にもこの制限があり、MS はおそらく合理的に聞こえると言って C# にもそれを入れた)、シャドーイングがプログラマーをつまずかせ、 C および C++ コミュニティ。そして彼らはおそらく、Lispers や Smalltalker や MLers などは、それを受け入れるか、いずれにしてもそのような低レベルの歩行者言語の使用を拒否するだろうと考えていました。

そして、私は人間工学の問題に対する彼らの評価に同意します。大きなコード ブロックを変更しているときに、新しいローカル バインディングを追加すると、誤って変数参照を取得する可能性があります。言い換えれば、テキストのプログラム編集は適切にレキシカルにスコープされていません。

于 2012-01-26T03:40:56.377 に答える