なんか1個、気になったプルリクが。
default(T)
を、左辺から型が推論できる場合にはdefault
だけで書けるようにしようっていう作業。
ちょっと前に提案があって、前向きっぽい感じではあったんですけど、なんで今実作業やってんだろう。 今からC# 7リリース辺りまでは、C# 7に入る予定の機能のバグ修正・改善ばっかりになると思ってたんですけども。 これくらいの修正ならさっと実装してC# 7に入れれそう?
まあ、これに限らず、今後、左辺から右辺の型推論は今後増えそうな感じです。
左辺と右辺
簡単に言葉の説明をしておくと、
左辺とか右辺ってのは、代入文の =
の左右から来てる言葉。
x = y
だったら、x
が左辺でy
が右辺です。
転じて、値を受け取る側が左辺で、値を渡す側が右辺。 変数宣言だと、宣言する変数が左辺で、初期値として渡す値が右辺です。 見た目は左右に分かれませんが、メソッド呼びだしなんかでも、値を受け取る側(仮引数)が左辺で値を渡す側(実引数)が右辺と考えられます。
右辺から左辺の型推論
C#の型推論って、右辺から左辺の推論が多いです。var
しかり、ジェネリック型引数しかり。
以下のような感じ。
class Program
{
static void Main()
{
var x = 1; // 1 からの推論で、int x = 1; 扱い。
F(2); // 2 からの推論で、F<int>(2); 扱い
}
static void F<T>(T x) => System.Console.WriteLine(typeof(T).Name);
}
左辺から右辺の型推論
逆に左辺から右辺の型推論なのは、現状ではラムダ式くらい。
using System;
class Program
{
static void Main()
{
Func<int, object> f = x => x.ToString(); // f の型が<int, string>なので、(int x) => (object)x.ToString() 扱い
X(x => x.ToString()); // Xの引数が<int, string>なので、(int x) => (object)x.ToString() 扱い
}
static void X(Func<int, object> f) { }
}
で、今、default
とnew
の型推論を増やしたいという話が出ています。特に、var
を使えないフィールドやプロパティに対する初期化子で有効そう。
class Sample<T>
where T : new()
{
static T newValue = new(); // new T() の T を省略
static T defaultValue = default; // default(T) の T を省略
static void F(T x = default) // default(T) の T を省略
{
}
}
で、なんか、default
の方はプロトタイプ実装が始まったと。
ちなみに、new
の方は実装の兆し全然なし。
C#チーム的に前向きに検討したいとは言ってるんですけども、実装コストはそこそこ高そう。
引数ありで、new(1, 2)
みたいなのもできる予定です。
補足: new()
とdefault
余談になりますが、「null
じゃダメなの?」とか、「new()
だけじゃダメなの?」とか言ってる人もまあ、います。
C#に不慣れな人には結構わかりにくいですよね…
以下のような差があるので、いずれも必要です。
new T()
だとインスタンスが作られる。null
にはならない-
null
は参照型にしか入らない。ジェネリック型引数T
とかだと、T
が参照型なのか値型なのか確定しないからnull
を使えないdefault(T)
だと、参照型ならnull
、値型なら「全メンバー0/null
初期化」
-
現状、値型であれば
new T()
とdefault(T)
はどちらも「全メンバー0/null
初期化」で、同じものになるけど、将来的に変えたい- 構造体にも引数なしのコンストラクターを定義できるようにしたい
new T()
は引数なしのコンストラクター呼び出しdefault(T)
は「全メンバー0/null
初期化」
ちなみに、構造体の引数なしコンストラクターの話は、一度C# 6に入りかかったんですけど、
Activator.CreateInstance
の内部に「構造体のnew T()
とdefault(T)
は同じ意味」っていう前提で最適化を掛けちゃってるコードがあるらしくて、
これを修正してもらわないとダメってことでrevertされました。
参考: