C#が参照の戻りをサポートしないのはなぜですか?


141

.NETは参照の戻りをサポートしていますが、C#はサポートしていないことを読みました。特別な理由はありますか?なぜ私は次のようなことができないのですか?

static ref int Max(ref int x, ref int y) 
{ 
  if (x > y) 
    return ref x; 
  else 
    return ref y; 
} 

8
ええと...引用してください?
RPM1984

5
C#には値と参照の型があり、どちらも意味を返すことができます。
再実行

私は次のようなことについて話しているreturn ref x
トムサルドゥイ

2
4年後、あなたはこれで正確にこれを行うことができますC# 7:)
Arghya C '17

回答:


188

この質問は、2011年6月23日の私のブログの主題でした。すばらしい質問をありがとう!

C#チームはC#7でこれを検討しています。詳細については、https://github.com/dotnet/roslyn/issues/5233を参照してください。

更新:この機能はC#7に組み込まれました!


あなたは正しいです; .NETは、変数へのマネージ参照を返すメソッドをサポートしています。.NETは、他の変数へのマネージ参照を含むローカル変数もサポートしています。(ただし、.NETは、ガベージコレクションのストーリーを非常に複雑にするため、他の変数へのマネージド参照を含むフィールドまたは配列をサポートしていません。また、「変数へのマネージド参照」タイプは、オブジェクト変換できないため、ジェネリック型またはメソッドへの型引数。)

コメンター「RPM1984」は何らかの理由でこの事実の引用を求めました。RPM1984この.NETの機能については、CLI仕様のパーティションIのセクション8.2.1.1「マネージドポインタと関連するタイプ」を読むことをお勧めします。

これらの両方の機能をサポートするバージョンのC#を作成することは完全に可能です。その後、次のようなことができます

static ref int Max(ref int x, ref int y) 
{ 
  if (x > y) 
    return ref x; 
  else 
    return ref y; 
} 

そしてそれを

int a = 123;
int b = 456; 
ref int c = ref Max(ref a, ref b); 
c += 100;
Console.WriteLine(b); // 556!

私はそうしたので、これらの機能をサポートするバージョンのC#をビルドすることが可能であることを経験的に知っています。高度なプログラマー、特にアンマネージC ++コードを移植する人々は、実際にポインターを使用してメモリをどこにでも固定するという大きなハンマーを使わずに、参照を使用してC ++に似た機能を要求することがよくあります。管理された参照を使用することにより、ガベージコレクションのパフォーマンスを台無しにするコストを支払うことなく、これらの利点を得ることができます。

私たちはこの機能を検討し、実際に実装して他の社内チームにフィードバックを表示できるようにしました。ただし、現時点では、Googleの調査によると、この機能は、実際にサポートされている言語機能にするための十分な広さや説得力のある使用例がないと考えています。他にも高い優先順位があり、利用できる時間と労力には限りがあるため、この機能をすぐに実行する予定はありません。

また、適切に行うには、CLRに変更を加える必要があります。現在、CLRはref-returningメソッドを正当である検証できないものとして扱います。これは、この状況を検出する検出器がないためです。

ref int M1(ref int x)
{
    return ref x;
}

ref int M2()
{
    int y = 123;
    return ref M1(ref y); // Trouble!
}

int M3()
{
    ref int z = ref M2();
    return z;
}

M3はM2のローカル変数の内容を返しますが、その変数の寿命は終了しています!スタックの安全性に明らかに違反しない ref-returnsの使用を決定する検出器を作成することは可能です。私たちがやろうとしていることは、そのような検出器を記述し、検出器がスタックの安全性を証明できなかった場合、プログラムのその部分でのref戻りの使用を許可しません。これを行うのは膨大な開発作業ではありませんが、実際にすべてのケースを確実に取得することを確認することは、テストチームの負担となります。これは機能のコストを増加させるもう1つの理由であり、現時点ではコストを上回るメリットはありません。

この機能が必要な理由を説明していただければ幸いです。実際の顧客がなぜそれを必要としているのかについての情報が多いほど、いつか製品に組み込まれる可能性が高くなります。かわいらしい機能ですので、興味がありましたらなんとかしてお客様にお届けしたいと思います。

(また、関連の質問を参照してください。C#で変数への参照を返すことは可能ですか?++私はCのようなC#関数の内部リファレンスを使用することはできますか?


4
@EricLippert:私には納得のいく例はありません。優れた説得力のある応答
Tom Sarduy、2011年

1
@エリック:あなたの例では、戻っy てきた後も生き続けるのに適していませんM2か?私はこの機能が地元住民を捕らえるラムダのように機能することを期待します。それとも、CLRがそのシナリオを処理する方法であるため、提案した動作ですか?
Fede

3
@Eric:プロパティが値型への参照を返すことができるという見方は、.net言語では大きな欠落です。Arrが値タイプの配列(例:ポイント)である場合、たとえばArr(3).X = 9と言うと、Arr(9).XまたはSomeOtherArray(2)の値を変更していないことがわかります。バツ; Arrが代わりに何らかの参照型の配列である場合、そのような保証は存在しません。配列のインデックス付け演算子が参照を返すという事実は非常に便利です。他の種類のコレクションがそのような機能を提供できないことは非常に残念です。
スーパーキャット2011年

4
エリック、見られる可能性を最大化するためにシナリオ(前の段落で提案したとおり)に関するフィードバックを提供する最良の方法は何ですか?MS Connect?UserVoice(任意の10の投稿/投票制限付き)?他に何か?
Roman Starkov

1
@ThunderGr:これは「安全でない」というC#の哲学です。メモリが安全でない可能性のあるコードを記述した場合、C#はそれを「安全でない」とマークし、メモリの安全性について責任を負うように要求します。問題の変数がアンマネージ型であるという条件で、C#には既に機能の安全でないバージョンがあります。問題は、C#チームがマネージ型を処理する安全でないバージョンを作成する必要があるかどうかです。そうすることで、開発者がひどいバグを簡単に記述できるようになれば、それは起こりません。C#はC ++ではなく、ひどいバグを簡単に記述できる言語です。C#は設計上安全です。
Eric Lippert

21

あなたは値型への参照を返すメソッドについて話している。私が知っている唯一のC#の組み込み例は、値型の配列アクセサーです。

public struct Point
{
    public int X { get; set; }
    public int Y { get; set; }
}

そして、その構造体の配列を作成します:

var points = new Point[10];
points[0].X = 1;
points[0].Y = 2;

この場合points[0]、配列インデクサーはstructへの参照を返します。これと同じ「参照を返す」動作を持つ独自のインデクサー(たとえば、カスタムコレクション用)を作成することはできません。

私はC#言語を設計しなかったので、それをサポートしないことの背後にあるすべての理由はわかりませんが、簡単な答えは次のとおりだと思います。


8
トムは、変数への参照返すメソッドについて尋ねています。変数は値型である必要はありませんが、もちろん、ref-returningメソッドが必要な場合は通常これが必要です。そうでなければ、素晴らしい分析。ユーザーが操作できる変数への参照を複雑な式が生成するC#言語の唯一の場所が配列インデクサーであることは正しいです。(もちろん、レシーバーとフィールドの間のメンバーアクセス演算子「。」ですが、これは明らかに変数へのアクセスです。)
Eric Lippert

1

あなたはいつも次のようなことをすることができます:

public delegate void MyByRefConsumer<T>(ref T val);

public void DoSomethingWithValueType(MyByRefConsumer<int> c)
{
        int x = 2;
        c(ref x);
        //Handle potentially changed x...
}

弊社のサイトを使用することにより、あなたは弊社のクッキーポリシーおよびプライバシーポリシーを読み、理解したものとみなされます。
Licensed under cc by-sa 3.0 with attribution required.