web-dev-qa-db-ja.com

C#プロパティとrefパラメータ、なぜ砂糖がないのですか?

私はC#で作業中にこのエラーメッセージに出くわしました

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

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

RefFn(ref obj.prop);

になる

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

プロパティが現在のコンテキストでgetおよびsetをサポートしていない場合、これは明らかに失敗します。

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


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

  • スレッディング
  • 例外

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

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


注:このエラーメッセージが生成される理由のメカニズムは理解しています。私が探しているのは、C#が些細な回避策を自動的に実装しない理由の根拠です。

70
BCS

ちょうど情報のために、C#4.0 will何かを持っていますlikeこのシュガーですが、相互運用メソッドを呼び出すときのみ-このシナリオではrefの性質が原因です。 (CTPで)あまりテストしていません。それがどのようにパンするかを見る必要があります...

11
Marc Gravell

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

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

32
David Morton

プロパティは、JavaスタイルのgetX/setXメソッドの構文糖衣にすぎません。メソッドの 'ref'にはあまり意味がありません。インスタンスでは、プロパティは単にフィールドをスタブするだけであり、プロパティは単にスタブである必要はないため、フレームワークはプロパティの 'ref'を許可できません。

[〜#〜] edit [〜#〜]:さて、単純な答えは、プロパティのgetterまたはsetterにフィールドの読み取り/書き込み以外のものを含めることができるという事実だけでは望ましくないということです。おそらく予想外のことは言うまでもなく、提案している種類の砂糖を許可します。これは、私が以前にこの機能を必要としていなかったということではなく、なぜ彼らがそれを提供したくないのかを理解しているということです。

17
user7116

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

9
Brian Rasmussen

スレッドセーフではありません。 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になります。

また、このコードサンプルは、匿名メソッドのようなものが、コンパイラーに甘いことをするように依頼する際に生じる困難を強調しています。

6
Mark Rendle

Ref/outを先頭に追加して渡すと、ヒープに格納されている参照型を渡すことになります。

プロパティは、変数ではなくラッパーメソッドです。

4
Igor Zelaya

これは、C#が参照渡しのパラメーターを受け入れる「パラメーターのある」プロパティをサポートしていないためです。 CLRはこの機能をサポートしますが、C#はサポートしないことに注意してください。

4
Andrew Hare

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

1
BC.

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

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

0
regex