メンバーを宣言するとき
class MyClass {
AnyClass<WithLong<Generic,Declaration>> myProp =
new AnyClass<WithLong<Generic,Declaration>>();
}
かなり冗長です。
次のような、より簡潔でノイズの少ないものを見つけることがあります。
AnyClass<WithLong<Generic,Declaration>> value = new default();
// or
AnyClass<WithLong<Generic,Declaration>> value = new();
// or
AnyClass<WithLong<Generic,Declaration>> value = new var();
実際の冗長性、または私が識別していないより簡潔な宣言に対するリスクはありますか?
注意:var
は、ローカル変数のノイズを減らすための実際の良い方法です。
var dictionary = new AnyClass<WithLong<Generic,Declaration>>();
私の質問は、その簡潔さをvar
構文が許可されていないメンバー宣言に拡張することについてです。
実際の冗長性、または私が識別していないより簡潔な宣言に対するリスクはありますか?
C#言語チームは、あなたが引用する構文例の1つをサポートすることにより、その冗長性の必要性を取り除くことについて議論しました :
class Foo
{
private AnyClass<WithLong<Generic,Declaration>> value = new();
...
}
現在はプロトタイプで、 C#9の場合は鉛筆で書かれています です。
C#言語チームは非常に保守的な束です。彼らは気まぐれで言語を変更せず、変更の長所と短所を厳密に分析します。アイデアとしてここまで進んでいるという事実は、その冗長性に客観的な利点がないことを強く示唆しています。
ただし、主観的なメリットがあります。一部の人々がvar
の使用を嫌うように:
var value = new AnyClass<WithLong<Generic,Declaration>>();
だからターゲット型の新しい表現を嫌う人もいるでしょう。そして多くの人々は、チームが同じことをする新しい方法を追加することを本当に嫌っています。チームはこれらの不満をメリットと比較検討し、この機能をリリースしないことを決定する可能性があります。時間がたてば分かる。
メンバー変数に対して提案している変更は、コンパイラーの変更のみです。実行中のCLRに変更を加える必要はありません。したがって、このような変更は簡単に実行できます。 Microsoftにリクエストを送信する必要があります。また、この冗長性は少し煩わしい(少しだけ)と感じました。ただし、1行に冗長性が含まれていると、落ち着きます。
開発者が冗長性を嫌う理由の1つは、バグ修正です。冗長なコードに関連するバグが修正されると、一方の場所を変更し、もう一方の場所を忘れてしまうことがあります。これは大きな問題です。この場合、冗長性は1行に含まれているため、その一部のみを変更する可能性はほとんどありません。また、これらのほとんどの場合、部分的に変更するとコンパイルエラーがスローされます。つまり、ある意味で大丈夫です。
クラス変数を宣言するときにvar
変数と同様の構文を使用すると問題が発生しません。
_public class Foo
{
var Bar = new Bar();
}
_
必要な情報はすべてそこにあり、あいまいではなく、既知の構文(変数の型推論)に従います。
ただし、宣言と初期化の組み合わせは、変数で発生する頻度とは対照的に、メソッドメンバーではほとんど発生しません。これが、言語アーキテクトが実際に取り組んでいない理由だと思います。
ご指摘のとおり、冗長性を減らすために、2つの冗長な参照のうちの1つを削減する必要があります。現在、このタイプの構文のみが許可されています(変数の場合):
_var dictionary = new Dictionary<T, int>();
_
これらのどちらでもない:
_Dictionary<T, int> dictionary = new();
Dictionary<T, int> dictionary = new var();
_
額面通り、そしてこの質問が主張しようとしていることから、これらは同様に有効であると思うでしょう。
あなたの例は、具体的なクラスのみを検討しているという点で少し制限されています。これがインターフェイス(または抽象クラス)でどのように機能するかを検討します。まず、現在の既存の方法:
_var userService = service.Get<IUserService>();
_
これは機能します。コンパイラーはGet<IUserService>()
の戻り値の型を識別し、次にvar
を同じ型(この例ではおそらくIUserService
)に設定します。
しかし、あなたが提案するアプローチについて:
_IUserService userService = new();
IUserService userService = new var();
_
これは機能しません。具体的なオブジェクトのインスタンス化または戻り値の型が定義されたメソッドがなければ、コンパイラーはここで何をすべきかを正確に決定することができません。
(インターフェースをインスタンス化できないため)既存の例でserviceメソッドを紹介するのは少しおかしいと思うかもしれませんが、問題の要点は、「type-first-var-last」アプローチが単に最終的に次のようになるため、このケースに削減を追加します。
_IUserService userService = service.Get<IUserService>();
_
=短縮なし。
これにより、既存のアプローチではすべての場合に短縮された一貫性のある構文が可能になるという単純な結論につながりますが、使用される型が具体的であるかどうかによって、提案は一貫性がないか適用不可能になります。
言い換えると、あなたの提案は、関連するすべてのケースをカバーすることができないため、既存のアプローチよりも単純にasではありません。
default
アプローチ上記では、私はあなたの最初の提案を省略しました:
_Dictionary<T, int> dictionary = new default();
_
default
には定義済みの動作があるため、一般的にはこれを避けます。参照型の場合、default(T)
は特にnull
を返します。
この提案を読んだとき、私の最初の要点は、変数にnull
を割り当てているということでした。構文は微妙に異なります(型パラメーターは使用されません)が、同じdefault
キーワードに実装されたこれらの非常に異なる動作を両方とも持つことには反対です。
技術的には、希望どおりに作成できます。しかし、読みやすさに悪影響を与えるので、私はそれが好きではありません。
この質問に真に答えるには、コンパイラーとリンカーがどのように機能するかを理解する必要があります。 C#は基本的に、これら2つの処理を1つのステップで実行します。変数が定義されている場所のコンテキストの違いにより、要求していることが実行可能かどうかのすべての違いが生じます。
グローバルレベルでは、クラスのサイズと形状を定義する必要があります。フィールド、メソッドなどは明示的かつ明確に定義する必要があります。その情報は通常、キャッシュされているため、コンパイラーがメソッド実装で作業しているときに、そのクラス定義構造を参照できます。
ここでの主要な問題の1つは、クラスレベルで左手のタイプを知っている必要があることです。これは、var
がまったく機能しない主な理由の1つです。したがって、var
を戻り値の型またはクラスフィールドの宣言として使用することはできません。
したがって、右側を見ると、作成する必要があるタイプを識別するための合理的な方法が必要です。左側の型はinterface
である可能性があるため(そして、私のコードでは多くの場合、それはインターフェースです)、省略形new()
だけに依存することはできません。
Java 8が提供する解決策は、ジェネリック仕様を単純化することでしたが、JavaのジェネリックはMicrosoftのジェネリックとは異なり、弱いコントラクトであるため可能です。宣言は次のようになります。
public class MyObject {
private Map<String,List<MyObject>> cache = new HashMap<>();
}
それはあなたのシナリオでは「十分に良い」かもしれません。そのコードをC#に変換するには、次のようになります。
public class MyObject
{
private IDictionary<string,IList<MyObject>> cache = new Dictionary<>();
}
ジェネリック宣言の煩わしい冗長性を減らしますが、フィールドの型定義で定義されたもの以外の実装を選択する表現力を提供します。
C#の新しいバージョンでこれに対処するための提案があるようですので、Microsoftがこの懸念に対処するかどうか/どのように対処するかを確認するのは興味深いでしょう。 参照 についてJacquesBに感謝します。