72

C#で作業しているときに、このエラーメッセージに遭遇しました

プロパティまたはインデクサーをoutまたはrefパラメーターとして渡すことはできません

私はこれの原因を知っており、正しいタイプのローカル変数を作成し、それをout/refパラメーターとして関数を呼び出してから、プロパティに割り当てるという簡単な解決策を実行しました。

RefFn(ref obj.prop);

になる

{
    var t = obj.prop;
    RefFn(ref t);
    obj.prop = t;
}

プロパティが現在のコンテキストでの取得と設定をサポートしていない場合、これは明らかに失敗します。

なぜC#は私のためにそれをしないのですか?


これが問題を引き起こす可能性がある場所を私が考えることができる唯一のケースは次のとおりです。

  • 糸脱毛
  • 例外

スレッド化の場合、その変換は書き込みが発生するタイミング(関数呼び出し後と関数呼び出し後)に影響しますが、それを信頼するコードは、壊れたときにほとんど共感を得られないのではないかと思います。

例外については、懸念事項は次のとおりです。ref関数がスローよりもいくつかのパラメーターの1つに割り当てるとどうなりますか?些細な解決策では、パラメータのすべてが割り当てられるか、割り当てられない場合があります。繰り返しになりますが、これは言語の使用がサポートされるとは思いません。


注:このエラーメッセージが生成される理由の仕組みを理解しています。私が探しているのは、C#が簡単な回避策を自動的に実装しない理由です。

4

9 に答える 9

31

これは、実際にはメソッド呼び出しの結果であるインデクサーの結果を渡しているためです。インデクサー プロパティにもセッターがあるという保証はありません。セッターが呼び出されずに自分のプロパティが設定されると開発者が考えた場合、ref によってそれを渡すと、開発者側で誤ったセキュリティが発生する可能性があります。

より技術的なレベルでは、ref と out は、渡されたオブジェクトのメモリ アドレスを渡します。プロパティを設定するには、setter を呼び出す必要があるため、特にプロパティ タイプが不変。ref と out は、メソッドの戻り時に値を設定するだけでなく、実際のメモリ参照をオブジェクト自体に渡します。

于 2009-02-09T20:34:47.407 に答える
16

プロパティは、Java スタイルの getX/setX メソッドに対するシンタックス シュガーにすぎません。メソッドの「ref」にはあまり意味がありません。あなたのインスタンスでは、プロパティは単にフィールドをスタブ化しているだけなので、それは理にかなっています。プロパティは単なるスタブである必要はないため、フレームワークはプロパティで 'ref' を許可できません。

編集:まあ、簡単な答えは、プロパティのゲッターまたはセッターが単なるフィールドの読み取り/書き込みよりもはるかに多くを含めることができるという単なる事実は、あなたが提案している種類の砂糖を許可することを望ましくなく、おそらく予期しないものにするということです. これは、私が以前にこの機能を必要としていないと言っているわけではありません。彼らがそれを提供したくない理由を理解しているだけです.

于 2009-02-09T20:36:05.080 に答える
12

参考までに、C# 4.0にはこの砂糖のようなものがありますが、相互運用メソッドを呼び出す場合のみrefです。(CTP で) あまりテストしていません。それがどのように展開するかを確認する必要があります...

于 2009-02-09T20:39:11.030 に答える
8

ref/でフィールドを使用できますoutが、プロパティは使用できません。その理由は、プロパティは実際には特別なメソッドの構文のショートカットに過ぎないからです。CLR はプロパティを直接サポートしていないため、コンパイラは実際に get / set プロパティを対応するget_Xandメソッドに変換します。set_X

于 2009-02-09T20:33:57.563 に答える
6

スレッドセーフではありません。2 つのスレッドがプロパティ値の独自のコピーを同時に作成し、それらを ref パラメーターとして関数に渡すと、そのうちの 1 つだけがプロパティに戻されます。

class Program
{
  static int PropertyX { get; set; }

  static void Main()
  {
    PropertyX = 0;

    // Sugared from: 
    // WaitCallback w = (o) => WaitAndIncrement(500, ref PropertyX);
    WaitCallback w = (o) => {
      int x1 = PropertyX;
      WaitAndIncrement(500, ref x1);
      PropertyX = x1;
    };
    // end sugar

    ThreadPool.QueueUserWorkItem(w);

    // Sugared from: 
    // WaitAndIncrement(1000, ref PropertyX);
    int x2 = PropertyX;      
    WaitAndIncrement(1000, ref x2);
    PropertyX = x2;
    // end sugar

    Console.WriteLine(PropertyX);
  }

  static void WaitAndIncrement(int wait, ref int i)
  {
    Thread.Sleep(wait);
    i++;
  }
}

PropertyX は最終的に 1 になりますが、フィールドまたはローカル変数は 2 になります。

このコード サンプルは、匿名メソッドなどによってコンパイラに甘い処理を要求する際の問題も浮き彫りにしています。

于 2009-02-09T21:46:27.470 に答える
5

これは、C# が、参照によって渡されるパラメーターを受け入れる "パラメーターフル" プロパティをサポートしていないためです。CLR はこの機能をサポートしていますが、C# はサポートしていません。

于 2009-02-09T20:41:55.847 に答える
4

先頭に ref/out を渡す場合、ヒープに格納されている参照型を渡すことを意味します。

プロパティはラッパー メソッドであり、変数ではありません。

于 2009-02-09T20:35:23.803 に答える
0

コンパイラがプロパティのゲッターによって返されたフィールドを置き換えない理由を尋ねている場合、それは、ゲッターが const または readonly またはリテラル、または再初期化または上書きしてはならない何かを返すことができるためです。

于 2009-02-09T20:40:10.093 に答える
0

このサイトには回避策があるようです。ただし、テストしていないため、動作することを保証することはできません。この例では、プロパティの get および set 関数にアクセスするためにリフレクションを使用しているように見えます。これはおそらく推奨されるアプローチではありませんが、求めていることを達成できる可能性があります。

http://www.codeproject.com/KB/cs/Passing_Properties_byref.aspx

于 2009-02-09T20:40:42.260 に答える