C#ではconstパラメーターが許可されないのはなぜですか?


102

特にC ++開発者にとっては奇妙に見えます。C ++ではconst、メソッドで状態が変更されないことを確認するために、パラメーターをとしてマークしていました。const refrefで渡すために渡すなど、他のC ++固有の理由もあり、状態が変更されないことを確認してください。しかし、なぜC#でメソッドパラメーターconstとしてマークできないのでしょうか。

メソッドを次のように宣言できないのはなぜですか?

    ....
    static void TestMethod1(const MyClass val)
    {}
    ....
    static void TestMethod2(const int val)
    {}
    ....

これは常にC ++の単なるセーフティキャッチ機能であり、私が見たように言語に本当に不可欠なものではありませんでした。C#の開発者は、それが明らかに多くの価値を追加するとは考えていませんでした。
Noldorin 2010

3
この質問でのより大きなディスカッション:「C#でのConst-correctness」:stackoverflow.com/questions/114149/const-correctness-in-c
tenpn

値タイプ[out / ref]にはありますが、参照タイプにはありません。面白い質問ですね。
ケニー

4
この質問には、C#と言語に依存しないタグの両方をどのように含めることができますか?
CJ7 2010

1
副作用のない不変のデータと関数は、論理的に考えるのが簡単です。F#fsharpforfunandprofit.com/posts/is
大佐パニック

回答:


59

他の良い答えに加えて、Cスタイルの定数をC#に入れない理由をもう1つ追加します。あなたが言った:

メソッドで状態が変更されないように、パラメーターをconstとしてマークします。

constが実際にそれを行った場合、それは素晴らしいことです。Constはそれを行いません。constは嘘です!

Constは、実際に使用できるという保証はありません。constを取るメソッドがあるとします。コード作成者は2つあります。発信者を作成する人と着信者を作成する人です。呼び出し先の作成者は、メソッドにconstをとらせました。2人の著者がオブジェクトについて不変であると仮定できるものは何ですか?

何もない。呼び出し先は自由にconstをキャストしてオブジェクトを変更できるため、呼び出し元は、constを取るメソッドを呼び出しても実際には変更されないという保証はありませ。同様に、呼び出し先は、オブジェクトの内容が呼び出し先のアクションを通じて変化しないとは想定できません。呼び出し先は、constオブジェクトの非constエイリアスでいくつかの変更メソッドを呼び出すことができましたが、いわゆるconstオブジェクトが変更されました

Cスタイルのconstは、オブジェクトが変更されないことを保証しないため、壊れます。現在、Cにはすでに弱い型システムがあり、本当に必要な場合は、doubleをintに再解釈してキャストできるため、constに関しても弱い型システムがあることは当然のことです。しかし、C#は適切な型システム、つまり「この変数に文字列が含まれている」と言うと、変数が文字列(またはnull)への参照を実際に含む型システムを持つように設計されています。型システムを嘘にしたくないので、Cスタイルの「const」修飾子を型システムに入れたくありません。コードについて正しく推論できるように、型システムを強くする必要があります。

CのConstはガイドラインです。それは基本的に「あなたが私にこれを変異させようとしないことを信頼できる」ことを意味します。それは型システムにあるべきではありません。型システムの内容は、その使用法のガイドラインではなく、推論できるオブジェクトに関する事実でなければなりません。

誤解しないでください。Cのconstが深く壊れているからといって、コンセプト全体が役に立たないというわけではありません。私が見たいのは、C#の「const」アノテーションの実際に正しく便利な形式です。このアノテーションは、人間とコンパイラの両方がコードを理解するために使用でき、ランタイムは自動並列化やその他の高度な最適化。

たとえば、コードの塊の周りに「ボックスを描く」ことができ、コンパイラがチェックできる方法で「このコードの塊がこのクラスのどのフィールドに対しても変更を行わないことを保証する」と言うことができると想像してください。または、「この純粋なメソッドはオブジェクトの内部状態を変更しますが、ボックスの外側で観察できる方法では変更しない」というボックスを描画します。このようなオブジェクトは、安全に自動的にマルチスレッド化することはできませんが、自動的にメモ化できます。リッチな最適化とより深い理解を可能にする、あらゆる種類の興味深い注釈をコードに付けることができます。弱いCスタイルのconstアノテーションよりも優れた方法を実行できます。

ただし、これは単なる推測にすぎません。私たちは、このような機能を将来の仮想バージョンのC#に組み込む計画はありません。これは私が見たいものであり、今後マルチコアコンピューティングに重点を置く必要があるかもしれませんが、C#の特定の機能または将来の方向性の予測または保証であると解釈されるべきではありません。

ここで、必要なのは、「このパラメーターの値はメソッド全体で変更されない」というパラメーターであるローカル変数の単なる注釈である場合、それは簡単に行えます。一度初期化される「読み取り専用」のローカルとパラメーター、およびメソッドで変更するコンパイル時エラーをサポートできます。「using」ステートメントによって宣言された変数は、すでにそのようなローカルです。すべてのローカルとパラメーターにオプションの注釈を追加して、変数を「使用する」ように動作させることができます。これは非常に優先度の高い機能ではなかったため、実装されたことはありません。


34
申し訳ありませんが、この議論は非常に弱いと思います。開発者が嘘を言うことができるという事実は、どの言語でも防ぐことはできません。オブジェクトaを変更するvoid foo(const A&a)は、梨の数を返すint countApples(Basket b)と同じです。それは単なるバグであり、あらゆる言語でコンパイルされるバグです。
Alessandro Teruzzi

55
「呼び出し先はconstを自由に捨てることができます…」ええと…(°_°)これはあなたがここで行っているかなり浅い議論です。呼び出し先は、でラン​​ダムなメモリ位置の書き込みを開始するだけでも自由0xDEADBEEFです。しかし、どちらも非常に悪いプログラミングであり、どの現代のコンパイラーによっても早くそして痛烈に捕らえられます。 今後、回答を書く際には、言語のバックドアを使用することで技術的に可能なことと、実際の実際のコードで可能なことを混同しないでください。このような歪んだ情報は、経験の少ない読者を混乱させ、SOの情報の質を低下させます。
Slipp D. Thompson 2015

8
「Consin Cはガイドラインです。」あなたが言っていることのいくつかの情報源を引用することは、ここであなたのケースを本当に助けるでしょう。私の教育と業界の経験では、引用されたステートメントは大げさです-Cのconstは型システム自体と同じくらい必須の組み込み機能です-両方とも必要なときに(Cのすべてのもののように)それらをだますためのバックドアがありますが、期待されています本でコードの99.99%使用されます。
Slipp D. Thompson、2015

29
この答えが、C#の設計をときどき嫌う理由です。言語設計者は、他の開発者よりもはるかに賢いことを確信し、バグから過度に保護しようとします。C ++ではメモリに直接アクセスでき、何でも好きなことができ、const宣言を丸める方法がいくつもあるので、constキーワードはコンパイル時にのみ機能することは明らかです。しかし、通常の状況では誰もこれを行いません。ちなみに、リフレクションを使用してこれらのメソッドまたはフィールドにアクセスできるため、C#の「プライベート」および「保護」も保証を提供しません。
BlackOverlord

13
@BlackOverlord:(1)自然に開発者を失敗ではなく成功に導く言語を設計したいという欲求は、あなたが推測しているように、デザイナーが顧客よりも賢いという信念ではなく、有用なツールを構築したいという欲求によって動機付けられいます。(2)リフレクションはC#言語の一部ではありません。ランタイムの一部です。リフレクションへのアクセスは、ランタイムのセキュリティシステムによって制限されます。低信頼コードには、プライベートリフレクションを実行する機能が付与されていません。保証アクセス修飾子によって提供さは以下にあり、低信頼コード。高信頼コードは何でもできます。
Eric Lippert 2016

24

C#にconstの正確性がない理由の1つは、ランタイムレベルで存在しないためです。ランタイムの一部でない限り、C#1.0には機能がなかったことを覚えておいてください。

また、CLRにconstの正確性の概念がないいくつかの理由は次のとおりです。

  1. ランタイムが複雑になります。その上、JVMにもそれがなく、CLRは基本的に、C ++のようなランタイムではなく、JVMのようなランタイムを作成するプロジェクトとして始まりました。
  2. ランタイムレベルでconstの正確性がある場合、BCLにconstの正確性があるはずです。そうでない場合、.NET Frameworkに関する限り、この機能はほとんど無意味です。
  3. BCLはconstの正しさを必要とする場合でも、CLRの上のすべての言語ですconstの正しさ(VBなどは、JavaScriptやPython、Rubyの、F#、)をサポートする必要がありません起きようが。

Constの正確性は、C ++にのみ存在する言語機能です。したがって、CLRがチェックされた例外を必要としない理由(Java言語のみの機能)と同じ議論に要約できます。

また、下位互換性を損なうことなく、このような基本的な型システムの機能を管理された環境に導入することはできないと思います。したがって、C#の世界に入る際のconstの正確さを当てにしないでください。


JVMにそれがないと確信していますか?私が知っているように、それはそれを持っています。Javaでpublic static void TestMethod(final int val)を試すことができます。
シークレット

2
Finalは本当にconstではありません。値/参照自体ではなく、参照IIRCのフィールドを変更できます。(いずれにしても値で渡されるので、パラメーター値を変更できないようにするためのコンパイラートリックです。)
Ruben

1
あなたの3番目の弾丸は部分的に真実です。BCL呼び出しのconstパラメータがあっても、コントラクトをより正確に説明するだけなので、重大な変更にはなりません。「このメソッドはオブジェクトの状態を変更しません」とは対照的に、「このメソッドではconst値を渡す必要があります」とは対照的です
Rune FS

2
これはメソッドで強制されるため、BCLで導入しても重大な変更にはなりません。
ルーンFS

1
ランタイムがそれを強制できなかったとしても、関数がrefパラメーターへの書き込みを許可/期待されるかどうかを指定する属性を持つことは有用です(特に、structメソッド/プロパティの場合this)。メソッドがrefパラメーターに書き込まないことを明確に示している場合、コンパイラーは読み取り専用の値を渡すことを許可する必要があります。逆に、メソッドがそれを書き込むと述べている場合this、コンパイラーはそのようなメソッドが読み取り専用の値で呼び出されることを許可してはなりません。
スーパーキャット2013

12

C#がconst-correctではない2つの理由があると思います。

最初は理解可能性です。const-correctnessを理解しているC ++プログラマはほとんどいません。の簡単な例const int argはかわいいですが、私も見ましたchar * const * const arg-非定数文字への定数ポインターへの定数ポインター。関数へのポインタのconst-correctnessは、まったく新しいレベルの難読化です。

2つ目は、クラス引数が値で渡される参照であるためです。これは、明らかに明確な構文なしで、処理するべき2つのレベルのconstnessがすでにあることを意味します。同様の障害は、コレクション(およびコレクションのコレクションなど)です。

定数の正確さは、C ++型システムの重要な部分です。理論的には、コンパイル時にのみチェックされるものとしてC#に追加できます(CLRに追加する必要はなく、constメンバーメソッドの概念が含まれていない限り、BCLには影響しません)。 。

しかし、これはありそうもないと思います。2番目の理由(構文)を解決するのは非常に難しく、1番目の理由(理解可能性)がさらに問題になります。


1
1が使用できるようCLRが本当に、この心配する必要がないことをあなたしている権利modopt及びmodreq(C + / CLIはすでにないよう-を参照してその情報を保存するために、メタデータレベルでSystem.Runtime.CompilerServices.IsConst)。これは、constメソッドにも簡単に拡張できます。
Pavel Minaev 2010

それは価値がありますが、タイプセーフな方法で行う必要があります。あなたが言及しているDeep / Shallow constは、ワームの缶全体を開きます。
user1496062 2014

参照が実際に操作されるC ++では、2種類のconstを持つことについてのあなたの議論を見ることができます。ただし、C#(「ポインター」を使用するにはバックドアを通過する必要があります)では、参照型(つまりクラス)には明確な意味があり、値には異なる意味がありますが、明確に定義されているように見えますタイプ(プリミティブ、構造体)
Assimilater

2
const-nessを理解できないプログラマは、C ++でプログラミングするべきではありません。
スティーブホワイト

5

constC#の「コンパイル時定数」を意味し、C ++のように「読み取り専用だが他のコードによって変更可能」ではない。constC#のC ++の大まかなアナログはですがreadonly、それはフィールドにのみ適用されます。それを除けば、C#にはC ++に似たconstの正確さの概念はまったくありません。

考えられる理由はたくさんあるので、その根拠をはっきりと伝えるのは難しいですが、私の考えでは、何よりもまず言語をシンプルにし、この2番目を実装することの不確実な利点を維持したいと思います。


したがって、MSはメソッドにconstパラメータがあると「ノイズ」のように考えると考えます。
シークレットモードで2010

1
「ノイズ」とは何なのかわかりません。むしろ「高コスト/ベネフィット比」と呼んでいます。ただし、もちろん、C#チームの担当者が確実に説明する必要があります。
Pavel Minaev

私が理解しているように、あなたの主張は、constnessを単純にするためにC#から除外されているということです。それについて考えてください。
スティーブホワイト

2

これは、C#7.2(2017年12月とVisual Studio 2017 15.5)から可能です。

構造体と基本タイプのみ!、クラスのメンバーではありません。

を使用inして、引数を参照として入力として送信する必要があります。見る:

参照:https : //docs.microsoft.com/en-us/dotnet/csharp/language-reference/keywords/in-parameter-modifier

あなたの例のために:

....
static void TestMethod1(in MyStruct val)
{
    val = new MyStruct;  // Error CS8331 Cannot assign to variable 'in MyStruct' because it is a readonly variable
    val.member = 0;  // Error CS8332 Cannot assign to a member of variable 'MyStruct' because it is a readonly variable
}
....
static void TestMethod2(in int val)
{
    val = 0;  // Error CS8331 Cannot assign to variable 'in int' because it is a readonly variable
}
....
弊社のサイトを使用することにより、あなたは弊社のクッキーポリシーおよびプライバシーポリシーを読み、理解したものとみなされます。
Licensed under cc by-sa 3.0 with attribution required.