なんか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) { }
}

で、今、defaultnewの型推論を増やしたいという話が出ています。特に、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されました。

参考: