C# Design Note 
岩永信之
Roslyn 
• http://roslyn.codeplex.com/ 
• C#/VBコンパイラー再設計プロジェクト 
• 製品名は「.NET Compiler Platform」になりそう 
• いいこと 
• MSの中の人がVisual Studioを作りやすくなる 
• サードパーティ製VSプラグインが作りやすくなる 
• これが一番の急務 
• おまけで、C#言語新機能も足しやすく
今日の話題 
• 新機能がどうというより、開発体制の変化 
• これまで 
• 製品版の2年は前にはCTP提供 
• 動くコンパイラー提供 
• この時点で仕様書結構かっちり/全機能分 
• 今 
• Roslynプロジェクトのオープン化 
• 最新機能を試したかったらcloneして自分でビルド 
• 緩い段階で仕様公開&ディスカッション/個別に機能追加
Discussions 
• Discussions under C# Language Design† 
• C#の言語機能についてのディスカッションページ 
• 誰でも投稿可能 
† http://roslyn.codeplex.com/discussions/topics/5998/c-language-design
Design Notes 
• C# Language Design Notes† 
• C#チームのデザインミーティング議事録※ 
• C#新機能、案を採用するか否か、実装の方針、懸 
念点など 
† https://roslyn.codeplex.com/wikipage?title=CSharp%20Language%20Design%20Notes 
※ ディスカッションとして投稿してて、誰でもコメント付け可能
例えば、VS “14” CTP 3新機能 
• 7/9のAgenda† 
1. Detailed design of nameof <details settled> 
2. Design of #pragma warning extensions <allow identifiers> 
7/9のDesign Notesの内容が 
そのままCTP 3(8/18公開)で実装されてる 
† https://roslyn.codeplex.com/discussions/552377
nameof 
• メンバー名を文字列化 
• renameリファクタリングできるstringリテラル 
class Point(double x, double y) : BindableBase 
{ 
double _x = x; 
double _y = y; 
public double X 
{ 
get { return _x; } 
set 
{ 
SetProperty(ref _x, value); 
OnPropertyChanged(nameof(Length)); 
} 
} 
public double Y 
{ 
get { return _y; } 
set 
{ 
SetProperty(ref _y, value); 
OnPropertyChanged(nameof(Length)); 
} 
} 
public double Length => Math.Sqrt(X * X + Y * Y); 
} 
…前略… 
SetProperty(ref _y, value); 
OnPropertyChanged(nameof(Length)); 
} 
} 
メンバー名を文字列化 
public double Length => Math.Sqrt(X * X + Y * Y);
#pragma warning extensions 
• サードパーティ製診断ツールのwarning抑止 
• 今まで: 
コンパイラー正規の警告を 
警告番号を指定して抑止
#pragma warning extensions 
• サードパーティ製診断ツールのwarning抑止 
• 追加: 
(Roslynを使って作った)VS拡張による警告を 
識別子名を使って抑止
この辺りは即断即決 
• nameofも#pragma拡張も、割と手早く仕様が 
決まって、問題なく実装されてる類 
• 悩ましいものも 
• Design Notes 8/27: parameterless constructors 
• Design Notes 9/3: declaration expressions
Parameterless Constructors 
• Design Notes 8/27† 
• The meeting focused on rounding out the feature 
set around structs. 
1. Allowing parameterless constructors in structs <allow, 
but some unresolved details> 
2. Definite assignment for imported structs <revert to 
Dev12 behavior> 
† https://roslyn.codeplex.com/discussions/562559
背景: 既定値 
• 値型の既定値は0クリア 
var points = new Point[1000]; 
• Pointが構造体のとき、コンストラクターを1000回呼ぶのか 
• 呼びたくないので、memset(0)にしたい
背景: new T() 
• 現状、new T()で既定値を作る 
var p = new Point(); 0クリア 
• 構造体はパラメーターなしのコンストラクターを持 
てない
背景: default(T) 
• .NET 2.0以降には、既定値用のdefault(T)がある 
var p = default(Point); 0クリア 
var p = new Point(); これで0クリアする必要まだある? 
• ちなみに 
コンストラクター呼んでもよくない? 
• .NET IL仕様上は構造体がパラメーターなしのコンス 
トラクター持てる 
• C#のコンパイラーレベルでエラーにしてる
問題: genericsのnew T()制約 
• new T() == default(T) 前提の最適化 
T M<T>() 
where T : new() 
{ 
return new T(); 
} 
Tが構造体の時、0クリアに 
最適化してしまう 
• new T() がコンストラクターを呼ぶようにするには 
ここも仕様変更に
問題: 既定の引数 
• 既定の引数= 定数のみ指定できる 
void M(S s = new S()) 
{ 
} 
• 現仕様(new S() == default(S)) なら 
問題なく使える 
• 新仕様(new S()はコンストラクター 
呼び出し)だとダメ 
変更必要 
void M(S s = default(S)) 
{ 
}
問題: T(…) : this() 
• コンストラクターから引数なしのコンストラク 
ターを呼ぶ場合 
struct S 
{ 
S(int x) : this() { X = x; } 
int X { get; set; } 
} 
• 引数なしコンストラクターを定 
義してある場合、どうなる? 
• 0クリアだけにしたい場合どう 
する? 
別構文が必要かも 
S(int x) : default() { X = x; }
検討: フィールド初期化子 
• 引数なしコンストラクターに伴って、 
構造体でもフィールド初期化子が使えるように 
struct S 
{ 
string label = "<unknown>"; 
bool pending = true; 
} 
構造体でもこういう 
書き方が可能に
検討: フィールド初期化子 
• 引数なしコンストラクターに伴って、 
構造体でもフィールド初期化子が使えるように 
ただし… 
• クラスに合わせて引数なしコンストラクターを自動 
生成する? 
• 今の構造体の仕様への追加になるように、引数なし 
のコンストラクターを明示的に書かないと初期化子 
動かなくする? 
• プライマリーコンストラクターがあるときはどう 
する?
Declaration Expressions 
• Design Notes 9/3† 
• The meeting focused on rounding out the design of 
declaration expressions 
1. Removing “spill out” from declaration expressions in 
simple statements <yes, remove> 
2. Same name declared in subsequent else-if’s <condition 
decls out of scope in else-branch> 
3. Add semicolon expressions <not in this version> 
4. Make variables in declaration expressions readonly 
<no> 
† http://roslyn.codeplex.com/discussions/565640
背景 
• declaration expressions自体はCTP 2で入ってる 
• 式の途中で変数宣言 
var n = int.TryParse(s, out var x) ? x : 0; 
if ((var x = obj as C) != null) { } 
else { } 
var y = (var x = GetValue()) * x; 
p.GetCoordinates(out var x, out var y); 
• 問題は、この変数xのスコープがどこまで続くか
検討: 宣言した変数のスコープ 
• 現仕様 
• 宣言後、ブロックの終わりまで 
{ 
var p = new Point(); 
p.GetCoordinates(out var x, out var y); 
var p = x * y; // OK 
Console.WriteLine("{0} = {1} × {2}", p, x, y); 
} 
// ここから先、x, y 使えない 
• 検討事項 
• ステートメント内に限るべきではないか 
• if-elseの場合、else句ではどうするか
検討: ステートメント内に限る 
• ほんとうにステートメントの外で使う? 
ステートメント内 
var n = int.TryParse(s, out var x) ? x : 0; 
var y = (var x = GetValue()) * x; 
p.GetCoordinates(out var x, out var y); 
var p = x * y; 
Console.WriteLine("{0} = {1} × {2}", p, x, y); 
xを使いたい範囲 
ステートメント内 
こいつ、要る? 
• C#だと、多値戻り値自体あまり推奨されてない 
• = 利用場面少ない 
• =レアケースのためにxのスコープ広げる? 
• ステートメント内に限った方がよくない?
検討: if-else 
• else句で使いたい? 
• →使いたい方がレアケース 
else句で意味ある値持ってない 
if (int.TryParse(s, out var x)) { } 
if ((var x = obj as A) != null) { } 
else if ((var x = obj as B) != null) { } 
else { } 
else句にスコープが漏れると 
むしろ使いにくい
検討: if-else 
• else句で使いたい? 
• else句に変数が残らないようにした場合の問題 
• if-elseで1つのステートメント 
→ 「変数のスコープはステートメント内で完結」ってシンプ 
ルなルールじゃなくなる(それより短い) 
• if (b) S1 else S2 
と 
if (!b) S2 else S1 
で意味が変わる
Pattern Matching 
• Draft spec for records and pattern-matching in 
C#† 
• C#にも型のパターンマッチングが入りそう 
• 現在はdraft 
• 実装もあり(まだmasterブランチには入ってない) 
† http://roslyn.codeplex.com/discussions/560339
パターンマッチング式 
• isを拡張 
if (exp is Add(Expression left, Expression right) { … } 
• switchも拡張 
switch (exp) 
{ 
case Add(Const(0), var x): return x; 
case Mul(Const(0), var x): return Const(0); 
}
operator is 
• 型判定+is演算子に展開 
static bool operator is(Cartesian c, out double x, out double y) 
{ 
x = c.X; 
y = c.Y; 
return true; 
}
record型 
• is, Equals, GetHashCode, ToStringの自動生成 
• プライマリコンストラクターの引数から 
プロパティを自動生成 
record class Point(int X, int Y) { } 
class Point(int x, int y) 
{ 
public int X { get; } = x; 
public int Y { get; } = y; 
public static bool operator is(…) { … } 
override public bool Equals(object obj) { … } 
override public int GetHashCode() { … } 
override public string ToString() { … } 
}
まとめ 
• http://roslyn.codeplex.com/ 
• 開発体制の変化: オープン化 
• 最新機能を試したかったらcloneして自分でビルド 
• 緩い段階で仕様公開&ディスカッション/個別に機 
能追加 
• 最近のディスカッション・仕様追加 
• 構造体の引数なしコンストラクター 
• 宣言式の変数スコープ 
• 型パターンマッチング

C# design note sep 2014

  • 1.
    C# Design Note 岩永信之
  • 2.
    Roslyn • http://roslyn.codeplex.com/ • C#/VBコンパイラー再設計プロジェクト • 製品名は「.NET Compiler Platform」になりそう • いいこと • MSの中の人がVisual Studioを作りやすくなる • サードパーティ製VSプラグインが作りやすくなる • これが一番の急務 • おまけで、C#言語新機能も足しやすく
  • 3.
    今日の話題 • 新機能がどうというより、開発体制の変化 • これまで • 製品版の2年は前にはCTP提供 • 動くコンパイラー提供 • この時点で仕様書結構かっちり/全機能分 • 今 • Roslynプロジェクトのオープン化 • 最新機能を試したかったらcloneして自分でビルド • 緩い段階で仕様公開&ディスカッション/個別に機能追加
  • 4.
    Discussions • Discussionsunder C# Language Design† • C#の言語機能についてのディスカッションページ • 誰でも投稿可能 † http://roslyn.codeplex.com/discussions/topics/5998/c-language-design
  • 5.
    Design Notes •C# Language Design Notes† • C#チームのデザインミーティング議事録※ • C#新機能、案を採用するか否か、実装の方針、懸 念点など † https://roslyn.codeplex.com/wikipage?title=CSharp%20Language%20Design%20Notes ※ ディスカッションとして投稿してて、誰でもコメント付け可能
  • 6.
    例えば、VS “14” CTP3新機能 • 7/9のAgenda† 1. Detailed design of nameof <details settled> 2. Design of #pragma warning extensions <allow identifiers> 7/9のDesign Notesの内容が そのままCTP 3(8/18公開)で実装されてる † https://roslyn.codeplex.com/discussions/552377
  • 7.
    nameof • メンバー名を文字列化 • renameリファクタリングできるstringリテラル class Point(double x, double y) : BindableBase { double _x = x; double _y = y; public double X { get { return _x; } set { SetProperty(ref _x, value); OnPropertyChanged(nameof(Length)); } } public double Y { get { return _y; } set { SetProperty(ref _y, value); OnPropertyChanged(nameof(Length)); } } public double Length => Math.Sqrt(X * X + Y * Y); } …前略… SetProperty(ref _y, value); OnPropertyChanged(nameof(Length)); } } メンバー名を文字列化 public double Length => Math.Sqrt(X * X + Y * Y);
  • 8.
    #pragma warning extensions • サードパーティ製診断ツールのwarning抑止 • 今まで: コンパイラー正規の警告を 警告番号を指定して抑止
  • 9.
    #pragma warning extensions • サードパーティ製診断ツールのwarning抑止 • 追加: (Roslynを使って作った)VS拡張による警告を 識別子名を使って抑止
  • 10.
    この辺りは即断即決 • nameofも#pragma拡張も、割と手早く仕様が 決まって、問題なく実装されてる類 • 悩ましいものも • Design Notes 8/27: parameterless constructors • Design Notes 9/3: declaration expressions
  • 11.
    Parameterless Constructors •Design Notes 8/27† • The meeting focused on rounding out the feature set around structs. 1. Allowing parameterless constructors in structs <allow, but some unresolved details> 2. Definite assignment for imported structs <revert to Dev12 behavior> † https://roslyn.codeplex.com/discussions/562559
  • 12.
    背景: 既定値 •値型の既定値は0クリア var points = new Point[1000]; • Pointが構造体のとき、コンストラクターを1000回呼ぶのか • 呼びたくないので、memset(0)にしたい
  • 13.
    背景: new T() • 現状、new T()で既定値を作る var p = new Point(); 0クリア • 構造体はパラメーターなしのコンストラクターを持 てない
  • 14.
    背景: default(T) •.NET 2.0以降には、既定値用のdefault(T)がある var p = default(Point); 0クリア var p = new Point(); これで0クリアする必要まだある? • ちなみに コンストラクター呼んでもよくない? • .NET IL仕様上は構造体がパラメーターなしのコンス トラクター持てる • C#のコンパイラーレベルでエラーにしてる
  • 15.
    問題: genericsのnew T()制約 • new T() == default(T) 前提の最適化 T M<T>() where T : new() { return new T(); } Tが構造体の時、0クリアに 最適化してしまう • new T() がコンストラクターを呼ぶようにするには ここも仕様変更に
  • 16.
    問題: 既定の引数 •既定の引数= 定数のみ指定できる void M(S s = new S()) { } • 現仕様(new S() == default(S)) なら 問題なく使える • 新仕様(new S()はコンストラクター 呼び出し)だとダメ 変更必要 void M(S s = default(S)) { }
  • 17.
    問題: T(…) :this() • コンストラクターから引数なしのコンストラク ターを呼ぶ場合 struct S { S(int x) : this() { X = x; } int X { get; set; } } • 引数なしコンストラクターを定 義してある場合、どうなる? • 0クリアだけにしたい場合どう する? 別構文が必要かも S(int x) : default() { X = x; }
  • 18.
    検討: フィールド初期化子 •引数なしコンストラクターに伴って、 構造体でもフィールド初期化子が使えるように struct S { string label = "<unknown>"; bool pending = true; } 構造体でもこういう 書き方が可能に
  • 19.
    検討: フィールド初期化子 •引数なしコンストラクターに伴って、 構造体でもフィールド初期化子が使えるように ただし… • クラスに合わせて引数なしコンストラクターを自動 生成する? • 今の構造体の仕様への追加になるように、引数なし のコンストラクターを明示的に書かないと初期化子 動かなくする? • プライマリーコンストラクターがあるときはどう する?
  • 20.
    Declaration Expressions •Design Notes 9/3† • The meeting focused on rounding out the design of declaration expressions 1. Removing “spill out” from declaration expressions in simple statements <yes, remove> 2. Same name declared in subsequent else-if’s <condition decls out of scope in else-branch> 3. Add semicolon expressions <not in this version> 4. Make variables in declaration expressions readonly <no> † http://roslyn.codeplex.com/discussions/565640
  • 21.
    背景 • declarationexpressions自体はCTP 2で入ってる • 式の途中で変数宣言 var n = int.TryParse(s, out var x) ? x : 0; if ((var x = obj as C) != null) { } else { } var y = (var x = GetValue()) * x; p.GetCoordinates(out var x, out var y); • 問題は、この変数xのスコープがどこまで続くか
  • 22.
    検討: 宣言した変数のスコープ •現仕様 • 宣言後、ブロックの終わりまで { var p = new Point(); p.GetCoordinates(out var x, out var y); var p = x * y; // OK Console.WriteLine("{0} = {1} × {2}", p, x, y); } // ここから先、x, y 使えない • 検討事項 • ステートメント内に限るべきではないか • if-elseの場合、else句ではどうするか
  • 23.
    検討: ステートメント内に限る •ほんとうにステートメントの外で使う? ステートメント内 var n = int.TryParse(s, out var x) ? x : 0; var y = (var x = GetValue()) * x; p.GetCoordinates(out var x, out var y); var p = x * y; Console.WriteLine("{0} = {1} × {2}", p, x, y); xを使いたい範囲 ステートメント内 こいつ、要る? • C#だと、多値戻り値自体あまり推奨されてない • = 利用場面少ない • =レアケースのためにxのスコープ広げる? • ステートメント内に限った方がよくない?
  • 24.
    検討: if-else •else句で使いたい? • →使いたい方がレアケース else句で意味ある値持ってない if (int.TryParse(s, out var x)) { } if ((var x = obj as A) != null) { } else if ((var x = obj as B) != null) { } else { } else句にスコープが漏れると むしろ使いにくい
  • 25.
    検討: if-else •else句で使いたい? • else句に変数が残らないようにした場合の問題 • if-elseで1つのステートメント → 「変数のスコープはステートメント内で完結」ってシンプ ルなルールじゃなくなる(それより短い) • if (b) S1 else S2 と if (!b) S2 else S1 で意味が変わる
  • 26.
    Pattern Matching •Draft spec for records and pattern-matching in C#† • C#にも型のパターンマッチングが入りそう • 現在はdraft • 実装もあり(まだmasterブランチには入ってない) † http://roslyn.codeplex.com/discussions/560339
  • 27.
    パターンマッチング式 • isを拡張 if (exp is Add(Expression left, Expression right) { … } • switchも拡張 switch (exp) { case Add(Const(0), var x): return x; case Mul(Const(0), var x): return Const(0); }
  • 28.
    operator is •型判定+is演算子に展開 static bool operator is(Cartesian c, out double x, out double y) { x = c.X; y = c.Y; return true; }
  • 29.
    record型 • is,Equals, GetHashCode, ToStringの自動生成 • プライマリコンストラクターの引数から プロパティを自動生成 record class Point(int X, int Y) { } class Point(int x, int y) { public int X { get; } = x; public int Y { get; } = y; public static bool operator is(…) { … } override public bool Equals(object obj) { … } override public int GetHashCode() { … } override public string ToString() { … } }
  • 30.
    まとめ • http://roslyn.codeplex.com/ • 開発体制の変化: オープン化 • 最新機能を試したかったらcloneして自分でビルド • 緩い段階で仕様公開&ディスカッション/個別に機 能追加 • 最近のディスカッション・仕様追加 • 構造体の引数なしコンストラクター • 宣言式の変数スコープ • 型パターンマッチング

Editor's Notes

  • #10 これとか、仕様書(案)見てても載ってないけど、実際にVS “14” CTP 3で試してみたらちゃんと動いた。