++C++; // 未確認飛行 C ブログ

http://ufcpp.net/

Orange Cube

with 3 comments

UnityのためのC#勉強会」に行ってきました。

弊社同僚 細田とでやったデモで使ったソースコードは GitHub 上に置いてあります。

https://github.com/ufcpp/UnityCsharp20150321

セッション資料の pptx もこの GitHub リポジトリ内に含まれています。

会社内の成果ベースでの発表は久しぶりでした。発表しようしようと思いつつもタイミングを逃し続け、気が付けば結構いろいろ仕上がってしまってて。ちょっと1回のセッションに一気につめこみすぎたかなぁとか思いつつの発表でした。

Written by ufcpp

2015年3月22日 at 00:24

カテゴリー: 未分類

ピックアップRoslyn 3/14

leave a comment »

今週のProposals

昔、「UnreachableAfter属性」で提案されていたもの。エラーチェックなどの際、必ず例外を投げるようなメソッドを作ることがあります。この場合、そのメソッドよりも後ろは絶対に実行されないわけですが、それをコンパイラーがわかるようにしたい(unreachableコード判定用)という要望があって、その解決策としての提案。

それが今回、属性じゃなくて、System.Neverっていう特別な型を作ってはどうかという提案が出ました。Scalaは、Nothing型で同様のことをしていて、そこからの着想だそうです。

Design Notes for Feb 11

2/11 の C# デザイン ミーティング議事録が今頃公開。

2/4 以降、議事録が issue ページ化されてなかったわけですが。

あんまりディスカッションしたいポイントがないからページ立てるモチベーションが低かったんですかね。議事録自体はあるみたいで、2/4のissueページの方で、「2/4以降のやつはないの?」とつつかれて「少しずつ出していく」との返事が返ってきてました。とりあえず、その翌週分、2/11のものがissueページ化。

内容的には、

  • Destructible types:
    • #161 の提案ベースだとダメそう
    • 「決定論的なオブジェクト破棄」というゴール自体はなくしたくない
    • IDisposable とか、既存コードとの親和性が高いよりよい方法が望まれる
  • Tuples
    • かなり乗り気なんだけど、もっと詰めないといけない点が何点か

という感じ。

タプルに関して、自分が興味ひかれたところだけ抜き出すと:

まず、タプル間の変換をどうしようという話。タプルの変換というと、以下のようなパターンがあり得るけども、どれがいいか(どれもダメっていう結論もあり得る)。少なくとも、reordering と renaming は絶対に両立できない。


(string name, int age) t = ("Johnny", 29);
/* Covariance */
(object name, int age) t1 = t;
/* Truncation */
(object name) t2 = t;
/* Reordering */
(int age, string name) t3 = t;
/* Renaming */
(string n, int a) t4 = t;

もう1つは、タプルの分解時に、変数名の補完が効くためには、受け手が右辺にないとダメよねという話。代入だと、受けてが左側に来るので、変数名を打ってる時点では右辺の型がわからず、補完が効かないのがめんどくさい(LINQ で、SQL もどきのくせに1行目が from なのも同じ理由。from から初めて、最後が select なら変数の補完が効く)。右辺側に代入するような新しい構文が要るかも(以下はその一例)。


Tally(myValues) ~> (var sum, var count); // strawman right side alternative
Console.WriteLine($"Sum: {sum}, count: {count}");

最後、out 引数を暗黙的にタプル化したいという話。F# だとできるので。例えば以下のような感じ。


bool TryGet(out int value){ … }
/* current style */
int value;
bool b = TryGet(out value);
/* New style */
(int value, bool b) = TryGet();

「C#スクリプト」パッケージ

工数的な問題で長らくいったん消えていたC#スクリプト関連のAPIが、NuGetパッケージに帰ってきそうです。

Roslyn プロジェクトはNuGetパッケージの毎夜ビルドにmygetを使ってるみたいなんですが、そのmyget(以下のURL)ビルドでは、スクリプトAPIが利用できるようになったとのこと。

RC2 扱い。RC2 のリリースいつだろう。

Written by ufcpp

2015年3月14日 at 18:09

カテゴリー: 未分類

ピックアップ Roslyn 3/7

with 2 comments

構造体の引数なしコンストラクター

今週は大変残念なお知らせが…

1つ目。構造体の引数なしコンストラクターの導入、やっぱり見送り(今のCTPだと実装されているけども、次のCTPで元に戻す)になるみたい。

構造体Tに対して、new T() が default(T) と同じ(0 初期化)になるという前提の最適化がすでにあちこちにあって、.NETのフレームワーク/ランタイムのレベルでもこの前提になっちゃってるところがあるみたい。で、new T() でコンストラクターが呼ばれないバグが、ランタイム レベルであって、今、C#に構造体の引数なしコンストラクターを導入するのは無理くさい。

個人的には、C# 6.0 に入って(入ることになってて)ありがたかった機能の結構上位にあったので残念。というか、一応名前が「RC (リリース候補)」になった矢先にですか…

今週のProposals

なんか今週は、「実現性薄そうだけどとりあえず議論しようか」感が漂っていたり。新しい提案 issue ページが結構な数「C# 7 and VB 15」マイルストーンに登録されていました。

enum のメンバーを、E.A | E.B みたいに書くんじゃなくて、型名を省略して A | B 的に書かせてくれという話。

実は、C# 6.0 の時点ですでに、using static を使って近いことはできるようになっています。


using System;
using static X;
[Flags]
enum X
{
A = 1,
B = 2,
C = 4,
}
class Program
{
static void Main(string[] args)
{
Console.WriteLine(A | B); // X.A | X. B
}
}

その上で、もう1歩踏み込みたい?という話。型の推論は効いてほしいかどうか。例えば、以下のコードをコンパイルできるようにしたいか。


using System;
using static Color;
using static Mood;
enum Color { Red, Blue }
enum Mood { Cheerful, Blue }
class Program
{
static void C(Color c) => Console.WriteLine($"color: {c}");
static void M(Mood m) => Console.WriteLine($"mood: {m}");
static void Main(string[] args)
{
C(Red);
M(Cheerful);
C(Blue); // Error CS0229 Ambiguity between 'Color.Blue' and 'Mood.Blue'
M(Blue); // Error CS0229
}
}

2つ目。C# の仕様的に、ジェネリックな属性クラスを作らせてほしいという話。

流れ的には、「メリット薄くない?」「実装手間考えるとあんまりやりたくない」的空気。

3つ目。静的メソッドも「拡張」できるようにしてほしいという話。

やりたいけど具体的な文法案はまだ固まっていない状態なので、このページのコメントでいろんな文法案が出てきています。

とはいえ、「インスタンスメソッド以外、プロパティとかも含めた拡張」という、もっと広いプランが C# 7.0 の当初から出ているので、おそらくはそっちに統合されるのではないかと。

this 相手に拡張メソッドを呼びたいとき、this.Method() って書くの面倒だし、インスタンス メソッドとの一貫性に書けるから this の省略を認めてほしいという話。こいつはページのコメント、いまいち盛り上がりに欠けてたり。

「クラス自身に対して拡張メソッド呼ぶの要る?自身に対してだったら普通のインスタンス メソッドを足せよ」という話があって、これができないわけですが。interface を明示的実装して、その interface の拡張メソッドを呼びたいということはまれにあるんですよね。ただ、稀にしかないことのためにコンパイラーの複雑化(拡張メソッドを探すのもそれなりのコスト)を招きたいかといわれると。

クラスとのフィールドとかに var を使わせてという話。

これが今まで認められていない理由は2つあります:

  1. (ポリシーとして) メンバーの型はドキュメントとして扱われるもので、省略すべきではない
  2. (実装上の問題として) 循環依存で死ぬ

循環依存は例えば以下のような感じ。


class A
{
public var N = B.M + 1;
}
class B
{
public var M = A.N – 1;
}

この問題を超えるメリットはないかなぁ…

あと、脱線で、const var を認めてほしい的な話も。const var って、constant (不変)で variable(可変)?variable が数学用語としての variable から来ているにしても、字面やばい。

ほぼ同機能を実装することになっているものの、C# に対してディスカッションが非常に少ない VB ですが。その VB で、AddressOf を省略させてという話。

Unicode Version Change in C# 6.0

こないだ、ufcpp.net 本体に「[雑記] C# ソースコードと Unicode」とか書いてたわけですけども。1か所、C# 6.0で破壊的変更になっちゃうもの(中黒「・」が識別子に使えなくなった)の話も書きました。

まあ、「Unicode の変更だ」といわれるとしょうがなく。Java 7 でも同様の問題出てて、しょうがないよね的な空気になっていたり。

なんですが、それはそれで、ドキュメント化は要るんじゃないかな、というか、今、Roslyn プロジェクト内で破壊的変更のドキュメント化作業もやってたよね。と、ふと思い立って、報告してみることに。そしたらその日のうちにドキュメントできてた。

最初にこの話題で盛り上がってたのはグラニの人ら(Build Insider の記事の下の方にその話題あり)なんですけども。今度会うとき言ってみとこう。

Written by ufcpp

2015年3月7日 at 18:05

カテゴリー: 未分類

Roslyn 2/28

leave a comment »

割と「週刊」化している、 https://github.com/dotnet/roslyn 内の今週の動き。

Milestone: C# 7

Milestone が「C# 7 and VB 15」なものに2項目追加。

プロパティ内限定(get と set の両方で使いたいけども、その他のメンバーからは触られたくない)スコープが欲しいという話。

C# 2.0 とか 3.0 の頃から欲しいとは言われていたものの、全然入らなかった機能。当時は自動プロパティ(3.0 で入った T X { get; set; } だけでプロパティが作れるやつ)だけでも、Anders (.NET のものすごく偉い人)がなかなか Go サインを出さなかったらしいですし、時代もだいぶ変わった感が。ここ数年、Anders はご意見番的な立ち位置で、Mads (C# チームのPM)の統括に代替わりできているみたいで。

仮想メソッドを派生クラスでオーバーライドする際に、戻り値に共変性を認めたいという話。例えば、class Derived : Base {} があるとき、ある規定クラスが virtual Base X() メソッドを持っていたとき、こいつの派生クラスは override Derived X() を定義したい。Java には昔からあって C# でもたまに欲しくなる話ではあります。

実装的には、.NET ランタイム(IL 仕様)の変更は不要そう。派生側に、override Base X() (基底クラスのやつのオーバーライド)と、new virtual Derived X() (別の仮装メソッドを追加)の両方を作って、override 側から new 側を呼び出すようなコードを生成すればいいはず(そういうやり方は、現在の C# では書けないものの、IL 仕様的にはできる)。

決定論的なモジュール ID

現状の Roslyn C#/VB コンパイラーは、コンパイルのたびに生成結果のモジュール(exe や dll)のバイナリが変わってしまうそうです。これは、ポータビリティの観点からあまりよろしくないので、決定論的な生成結果を得るための調査を始めた模様。

一番の原因は、モジュールの ID にタイムスタンプが入っているかららしいです。これを、モジュールのハッシュ値みたいなものに変えたいというのがまず第一。ただし、これが決定論的であるためには、タイムスタンプに実際のコンパイル時刻などは使えなくなります。ちゃんとした時刻が入っていることを期待したプログラムの動作保証がなくなります。

そして、ハッシュ値が決定論的であるために、コンパイラーが生成するモジュール自体が決定論的である必要があって、そのためにいくつか別の課題が上がっています。

Written by ufcpp

2015年2月28日 at 19:47

カテゴリー: 未分類

VS 2015 CTP 6、Microsoft.CodeAnalysis.Analyzers RC 1

leave a comment »

昨日、Visual Studio 2015 の CTP 6 が出たみたいですが。

まだRC(リリース候補)じゃなくてCTP(テクニカル プレビュー)なんですねぇ。

一方で、Microsoft.CodeAnalysis.Analyzers (Roslyn の NuGet パッケージ配布)は RC1 になりました。Visual Studio 2015 CTP 6 の Roslyn (要するに、CTP 6 が使っている C# コンパイラー)はこの RC 1 のやつだそうです。

つまり、C# 6.0 は RC 1 になりました。

CTP 6 での C# 6.0 の更新点

バグフィックスとかは置いておいて、C# 6.0 の文法的な話でいうと、2点更新があります。

  • Exception Filters の仕様変更
  • IFormattable な String Interpolation

Exception Filters の仕様変更

例外フィルターで、if ではなく、when を使うようになりました(新しい文脈キーワード。catch の条件句の直後でだけキーワードになる)。


try
{
}
catch (ArgumentException e) when (e.ParamName == "x") // 前まで if だった
{
// パラメーター名が x の時だけはエラー無視
}

if だとね… 波かっこ {} の有無で意味が変わっちゃうとかいう恐ろしい仕様だったので。これは仕方がない。

IFormattable な String Interpolation

「C# 6.0 正式版までには入る」機能のうちの最後の1つ、FormatProvider 指定できるバージョンの文字列補間が実装されました。


private static readonly IEnumerable<string> Cultures = new[] { "en-us", "fr", "zh-hk", "ja-jp" };
public static void Y()
{
var x = 10;
IFormattable f = $"{x :c}, {x :n}";
foreach (var c in Cultures)
{
var s = WithCulture(f, c);
Console.WriteLine(s);
}
}
public static void SameAsY()
{
var x = 10;
IFormattable f = System.Runtime.CompilerServices.FormattableStringFactory.Create("{0:c}, {1:n}", x, x);
foreach (var c in Cultures)
{
var s = WithCulture(f, c);
Console.WriteLine(s);
}
}
private static string WithCulture(IFormattable f, string cultureName)
{
return f.ToString(null, System.Globalization.CultureInfo.CreateSpecificCulture(cultureName));
}

これが、C# 6.0 の新機能の中で唯一、.NET Framework のバージョンアップする(もしくは、同シグネチャのクラスを自前で追加実装してやる)必要のある機能。上記コード例は、ターゲットを .NET Framework 4.6 にするか、FormattableStringFactory/FormattableString クラスを自作しないと動きません。

ちなみに、FormattableString は、当初、構造体にするかもという話も出ていましたが、結局クラスになったようです。あと、$”” の展開結果は、直接 new FormattableString(format, args) するんじゃなくて、ファクトリ メソッド FormattableStringFactory.Create(format, args) を介して作られるようになったみたいです。後からの拡張ができるようにしてあるのかも。

Written by ufcpp

2015年2月26日 at 00:09

カテゴリー: 未分類

Roslyn 2/21

leave a comment »

https://github.com/dotnet/roslyn 内の動きから、今週もいくつかネタを。

バージョン違いでコンパイル

そういや先日のネタの為に、複数バージョンのC#コンパイラーを同時に読んで、結果を色分けして表示するPowerShellスクリプトを書いたわけですが、その後も度々活躍していたり。Roslynのリポジトリ見てると結構、古いバージョンだとどうだったんだっけ?とか気になったり。

ということで、このスクリプトもGistにでも置いておきます。


cls
. C:\Windows\Microsoft.NET\Framework64\v2.0.50727\csc.exe .\a.cs | Write-Host -ForegroundColor DarkCyan
. C:\Windows\Microsoft.NET\Framework64\v3.5\csc.exe .\a.cs | Write-Host -ForegroundColor DarkMagenta
. C:\Windows\Microsoft.NET\Framework64\v4.0.30319\csc.exe .\a.cs | Write-Host -ForegroundColor DarkGreen
. 'C:\Program Files (x86)\MSBuild\12.0\Bin\csc.exe' .\a.cs | Write-Host -ForegroundColor DarkRed
. 'C:\Program Files (x86)\MSBuild\14.0\Bin\csc.exe' .\a.cs | Write-Host -ForegroundColor DarkBlue

view raw

csc.ps1

hosted with ❤ by GitHub

StaticClassInstance

gitter運用始まりました

Miguel(Monoの偉い人)がgitter立ててた。

https://gitter.im/dotnet/roslyn

gitterについて紹介しておくと、GitHubと連携して、リポジトリや組織単位の部屋を立てれるチャットサービス。発言はGitHub互換のmarkdownで書けるし、#123とか書くとGitHubの該当issueへのリンクになるし、:+1:とかでの絵文字も使えるみたい。

 Portable PDB

Portable PDB (program database)の仕様が公開されたみたい。

https://github.com/dotnet/roslyn/blob/portable-pdb/docs/specs/PortablePdb-Metadata.md

PDBは、デバッグとかビルド用にプロジェクト内のソースコードの状態が色々記録されてるファイル。例えば、スタックトレースとかブレイクポイント用に実行ファイルのどこがどのソースコードの何行目かとかの情報が入っていたり。あと、インクリメンタルビルドのためとかにも使っているみたいです。

これまでは、一応PDB読み込み用のライブラリは公開されてたりはするものの、詳しいドキュメントはいっさいなかったみたいです。

この手のデータは、このMSが使ってるPDB以外でも、大抵のコンパイラーがそれぞれ独自の形式を持ってたりします。ほんとバラバラ。C#でも、MS製コンパイラーはPDBを使っていますが、MonoのmcsコンパイラーはMDBっていう別形式を持っています。

今回、オープンになった形式(Portable PDB)は、.NETのメタデータの上位互換なフォーマットみたい(物理フォーマットは同じで、メタデータと同じ実行ファイルに混ぜることも可能。要は論理スキーマの追加)。

まあ、デバッグに必要な情報はかなりの部分メタデータと被るので。世の中には「C++でもリフレクションできるよ?PDB読めば」とかいう猛者もいらっしゃるくらいで。ちなみに、「PDBってVC++専用でしょ?」って聞くと、「もちろんコンパイラーごとに別実装する」との返事が返って来たり。

などという深い闇を産んできたわけですが、オープン化で状況改善しそうでほんとになにより。

default(StaticType)

静的クラスがらみのバグフィックスと、それの結果、一部旧コンパイラーから破壊的変更になったという話が1件ドキュメント化。

https://github.com/dotnet/roslyn/blob/master/docs/compilers/CSharp/Static%20Type%20Constraints.md

C#の仕様的に、静的クラス(static修飾を付けたクラス)は絶対にインスタンス化できないわけで、静的クラスの変数はそもそも作れなくしてあります(コンパイルエラー)。

静的クラスAがあったとき、

A x;

はエラーになります。これくらい素直な場合はいいんですが、めんどくさいケースがいくつかあります。まずは、varでの変数型推論。

var x = default(A)

そもそも、default(A)を認めるなよって話ではあるんですが、旧コンパイラーでは default(A) を書けちゃうそうです。Roslyn新コンパイラーでも、互換性のため、default(A)自体は書けるようにした(バグだけど直さない)とのこと。

そして、そのせいで、先ほどのvar x = default(A); が問題に。別途、「型推論の結果、varが静的クラスになるのは認めない」というルールを設定。

次にはまったのは、これをジェネリックなメソッドに渡す場合。void M<T>(T x)がある時の、

M(default(A));

これも別途、推論結果で型パラメーターが静的クラスになったときをエラーにしないといけない(最初、コンパイルエラーになってなくて、バグ報告されてた)。

そして、これとは別に、旧コンパイラーにはもう一個バグがあって、

((dynamic)3).M<StaticType>()

これがコンパイル通るそうです。dynamicが絡むとチェックが甘く。Roslynではこれは直したそうで、破壊的変更になってしまっているとか。まあ、実行時エラーになって他ものがコンパイルエラーになるだけですけども。

Roslyn内のコードをいくつかC#6化

試しにアナライザーのいくつかをC# 6を使って書き換えてみたそうです。

https://github.com/dotnet/roslyn/pull/740

目的は「少しやってみて、皆の印象を聞きたい」みたいな感じみたい。なのでごく一部だけ(Roslynのアナライザーを使えば機械的に全置換とかもできるけど、それはやってない)みたい。

Written by ufcpp

2015年2月21日 at 23:52

カテゴリー: 未分類

Roslyn コンパイラー仕様

with one comment

まだ pull-req 通ってないんですが、Roslyn リポジトリ上にこんなものが。

https://github.com/dotnet/roslyn/pull/536

コンパイラー仕様のドキュメント化、始めました。

“コンパイラー仕様”

コンパイラーを作っていると、標準化されている言語仕様では不十分な仕様ってものがどうしても出てきます。C# コンパイラーにもいくつかそういうものがあるんですが、「オープン化したんだからそういう隠れ仕様もドキュメント化しないとダメだよね」という感じで、その手始めに、コンパイラーチームの内部 OneNote で書かれていた仕様を .md 化してリポジトリに追加しようとしているみたい。pull-req が通った暁には、/docs/compilers フォルダー以下にこういう “コンパイラー仕様” が並びます。

具体的にどういうものをドキュメント化しようとしているかというと、

  • コマンドライン スイッチとその意味
  • 以前のバージョンのコンパイラーからの破壊的変更
  • 標準仕様に反するコンパイラーの内部挙動
  • 言語仕様に記述されていないコンパイラー機能
    • COM 関連や、マイクロソフト特化の動作
    • コンパイラーの挙動に影響を与える “well-known” な属性(Obsolete とか Conditional のこと)
    • “ruleset” (FxCop 的なものの、どの警告を出してどの警告は抑止するみたいなルール)のファイル構文(syntax)と意味論(semantics)
  • C#とVB間の相互運用のための機能
    • 名前付きインデクサー(インデックス付きプロパティ)のC#からの利用
  • 言語仕様で明確でなく、発散の余地のあるコンパイラー挙動
  • 制限次項(例えば識別子長など)
  • バージョンごとの変更履歴

など。

そんなのよく気づいたな

今回公開されたコンパイラー仕様のうちの1つ、「Definite Assignment」がネタとして面白かったんで紹介。要するに、「変数には確実に値を与えておけ」仕様に関する話。

C#では、未初期化のローカル変数の参照を認めていません。これ自体は言語仕様にも書かれていることです。問題は、「何をもって未初期化・初期化済みを判定するか」という部分。判定ロジックに関する詳細が言語仕様に足りていないので、「明確でなく、発散の余地のある挙動」になっているみたいです。

例えば、このページで紹介されている例は以下のようなもの。


class Program
{
static void Main(string[] args)
{
int x;
if (false && x == 3) // 恒偽定数式の後ろの、ショートサーキット演算に続く式は絶対に到達できない
{
x = x + 1;
}
}
}

C#コンパイラーは、到達不能な場所では「未初期化変数を使った」判定をしないみたいです。つまり、 if (false) { ここ } とか、if (true) {} else { ここ } とかでは、別に未初期化変数を使ってもコンパイルエラーにならない。

で、コンパイラー挙動がぶれているのは、さらに、ショートサーキット演算(&& とか || とか、左辺の時点で結果が確定したら右辺を実装しないもの)と組み合わさった場合。false && (ここ) とか true || (ここ) とかは絶対に通らないわけで、前述の if の例に習うなら、別に「未初期化変数を使った」判定をさぼってもいいはず。

ところが…

DefiniteAssignmentUnreachableCode

C# 3.0~5.0 まではエラーになるんですって、これ。C# 2.0の頃はエラーになってなかった。そして、Roslynでもエラーにならなくした。というか、明確なルールを加えた。とのこと。

言われるまで気づかなかった… まあ、こんなコード書かないし、誰も気づかないから言語仕様に漏れるんですが。

なんなんですかね、これ。パターン マッチングとか宣言式とかを実装する過程でこれに類するコードが生成されちゃって困ったとかがあったんですかねぇ。それとも、誰かC# 2.0の頃にこの類のコードを書いてる人がいて、C# 3.0の時に「破壊的変更」を不具合報告入れたとか?

Written by ufcpp

2015年2月17日 at 23:39

カテゴリー: C#

Tagged with

Roslyn issues 2015/2/16

leave a comment »

前回でひと段落したC# 7提案関連の話、一応リンクまとめておきますか。

そして今後は週1程度で個人的に興味持ったものをピックアップしていこうかなぁという感じなわけですが。

とりあえず、第1回。

Non-nullable references: a few difficulties and possible solutions

https://github.com/dotnet/roslyn/issues/227#issuecomment-73928184

非null許容な参照型に関する issue ページに、Mads (C# チームの偉い人)が結構まとまった内容のコメントを付けてた。

要点は

  • フィールドだと他の場所で書き替えられる可能性があるから、非null保証しにくい
    • 参照型 T を、非null参照型 T! の変数で受けてから使う、みたいな処理が必要
    • パターンマッチングを使えば、x is T! y みたいな書き方はできる
  • 規定値どうしよう
    • 参照型の規定値は null なんで、既定でない値の代入が必須
    • 特に配列とかout 引数の場合に心配
  • ライブラリの互換性
    • 非null参照型が入ったら、既存のライブラリをぜひともこれに対応させたいって思う人がかなり多いはずだけども、既存の参照型 T を非null参照型 T! に書き替えると破壊的変更になる
    • コードを壊さないようにするためには T から T! への暗黙的変換が必要。でも、暗黙的にしてしまうと、せっかくの非null型の魅力半減

という感じ。

Avoid unnecessary boxing with String.Concat

https://github.com/dotnet/roslyn/pull/415

C# で、”int ” + 1 みたいな書き方(文字列 + その他の何か)をすると、string.Concat 呼び出しにコンパイルされます。この例で言うと、string.Concat(“int”, 1)。

で、string.Concat の引数は params object[] です。整数とか、値型に対して使うとボックス化が発生。

で、この無駄なボックス化を避けるために、値型だった場合、先に ToString メソッドを呼んでしまおうという提案がされています。もちろん挙動変更なので慎重に検討。

とりあえず、この挙動変更で問題が出るようなプログラムはなさそうには思います。また、C# の仕様書を読み合わせてみても、問題のある(仕様違反になる)変更ではないはず。

What language proposals would benefit from CLR changes?

https://github.com/dotnet/roslyn/issues/420

C# 6までは、とりあえずコンパイラーの修正だけで実現できる機能ばっかりでした。

C# 7の提案に入ってからは、まずは要望だけ洗いだしている状態で、コンパイラーだけでやるべきか、.NETランタイムに手を入れるべきかも含めて検討中。

で、今週、「.NETランタイムにも手を入れるならこういうこともできるよ。他に何か要望ある?」ページが建ちました。

Improve memory managment in async methods

非同期メソッドやイテレーター中でローカル変数を定義すると、コンパイル結果的にはクラス生成されて、ローカル変数だったものが実はフィールドに格上げされます。これは、awaitやyield returnを超えて同じ変数を使うためには必要な処置です。

問題は、awaitを超えて変数を使わない場合。awaitを超えないということは、別にフィールドにしなくても挙動は変わりません。ところが、現状のC#コンパイラーは、awaitを超えない場合でも変数をフィールドの格上げします。理由は、デバッグ時、非同期メソッド内に break point を仕掛けて止めた場合、awaitより前の変数の状態を見たいときがあるから。つまり、デバッグのためだけに、無駄な処理を行っています(ローカル変数のフィールド化は、メモリ管理上、結構な無駄)。

この挙動変える?break point で止めた時に await より前の変数覗きたい?デバッグ ビルドの時だけフィールドに格上げすべき?みたいな内容の issue ページ。

Written by ufcpp

2015年2月16日 at 02:20

カテゴリー: C#

Tagged with

C# 7に向けて(9): Method Contracts

with one comment

今 issue ページができてて大きめのものはこれが最後のはず。Method Contracts。メソッドに対する「契約」。

結構長かった… 結局、全9回に。今後は、「週刊ブログ」程度でよくなるはず。きっと。

契約プログラミング

そもそも「contracts」とは何か的な話は、昔書いたスライド貼って済まそう。

(補足: このスライドでは非null制約を例に挙げて説明していますが、非null制約は契約でやるよりも、「非null参照型」を作る方がいいと思います。C# 7向けの提案の中にはこの「非null参照型」も含まれています。)

一応、.NET 4の頃から、ライブラリとツールでのサポートはありました。


public int Insert(T item, int index)
{
Contract.Requires(index >= 0 && index <= Count);
Contract.Ensures(Contract.Result<int>() >= 0 && Contract.Result<int>() < Count);
return InsertCore(item, index);
}

view raw

Contracts.cs

hosted with ❤ by GitHub

また、研究的な位置づけのプロジェクトでは、C# に契約プログラミング機能を足したプログラミング言語(Spec#)もありました。

まあでも、C# が言語機能レベルでサポートしないと流行らないよね、きっと。という状態。

問題

.NET 4で契約がらみのクラス(Contract)が追加されてからも、実際のところ、このContractクラスを使ってくれたライブラリは少ないです。

どういう問題があるかというと例えば、

  • Contract. を付けないと行けなくていちいちめんどくさい
  • 契約の宣言場所がメソッド本体の中(本来、外に見せない内部実装にあたる場所)にある
    • 契約は外から見えるべき
  • ポスト プロセスが必要
    • Contract.Requires/Ensures を書いている場所では実際には何も起こらず、コンパイル結果のILをさらに書き替えてチェックを行う仕組みになってる
    • ドキュメントへの反映(「このメソッドはこういう契約を持っている」みたいなのを doc コメントに反映)もポスト プロセス
  • ソースコードの静的解析が結構重い
  • Visual Studio の上位エディション(確か、当初、Ultimate 限定?Premium でも行けたかも)が必要
    • ポスト プロセス、静的解析ともに

提案: C# 言語機能での契約

文法的には Spec# の頃とあんまり変わらないものになりそう。


public int Insert(T item, int index)
requires index >= 0 && index <= Count
ensures return >= 0 && return < Count
{
return InsertCore(item, index);
}

view raw

Contrasts.cs

hosted with ❤ by GitHub

requires (事前条件: 引数が満たすべき条件) や ensures (事後条件: 戻り値が見たすべき条件)の違反は fail-fast (エラー ログを残して即座にプログラム終了)にするようです。契約は、理想を言えばコンパイラーによって違反は全部コンパイル エラーにしてしまう方がいいものです(単に、技術的困難から実行時に残るだけ)。テストによって契約違反は取りきるべき(ちゃんと、呼び出し元がすべて責任をもって引数チェックしてからメソッドを呼ぶ)もの。なので、規定動作は例外すら出さず、強制終了。

一応、requires/ensures の後ろに else throw を付けて、この挙動を変える(例外を投げるだけにする)機能は検討されているようです。

あと、呼び出し元側で責任をもってチェックする必要があるということは、requires/ensures 句内では、メソッドよりもアクセス制限の強いメンバーの参照は禁止されるべきです。internal なメソッドの契約で private フィールドを参照したりはできません。

Written by ufcpp

2015年2月11日 at 19:30

カテゴリー: C#

Tagged with

C# Design Notes 2/4版

with one comment

新しいC#デザイン ミーティング議事録。

今回の議題は3つ。大きいのは3つ目の Classes with values です。タプルと関連してのレコード再検討。

あっ、あと、おまけで。Roslyn の RC1 を NuGet 公開したそうです。

Internal implementation only

前にC# 7シリーズその(6)で触れた、 ImternalImplementationOnly 属性に関して、もう少し詳細な検討。

問題点に関しても前の issue ページより少し詳細に。そして、今でもできること、コンパイラーに機能追加すればできること、.NET ランタイムにも手を入れるならみたいな話と、それでどのくらい「穴」が改善するかという話がかかれています。

問題はインターフェイスに後からメソッドを足しにくい(一般にもやっては行けないこととされているし、BCL チームのポリシー的には完全にNG)ことなので、こういう属性を足す以外にも、mixins/traits って呼ばれるような言語機能の追加でも問題は回避できるかもしれません。

実際に C# 6 や 7 で、どこまでやるかや、開発ポリシーをどうしていくかなども含めて、BCL チームとの対話も必要そう。

Tuples, resords, deconstruction

その(8)で書いた通り、C#7 に向けて、タプル型も提案されたわけですが。タプル型が入ることによって、レコード型やパターン マッチングについても再検討が必要という話。

特に、タプルとレコードについて、これらの機能は一元化する方がいいかもしれないし、少なくとも統合的にデザインしていく必要があるでしょう。タプルは「レコードの特別な場合」であるべきか、もしかしたらレコードは単なる「型名のあるタプル」であるべきかなどということも考えられます。

レコードの導入動機は、値セマンティクスや immutability を持つクラスを楽に作れるようにするものなので、この辺りを次節で検討します。

Classes with values

今回出たアイディアは以下のようなもの。


// Point の実際の値を持つ構造体。
// 現状の C# だと自分で実際にこういう型定義を書く必要があるけども、タプルにできるのでは。
struct PointValue
{
public int X;
public int Y;
}
// 値セマンティクスを持つ immutable クラス
// レコード型の実装はこうあるべきだろうという内容。
// ここでいう値セマンティクス(value semantics)は、全メンバー比較で Equals、全メンバーのハッシュ値から GetHashCode 計算できるもののこと。
class Point
{
// 唯一の構造体メンバーに readonly を付けているので immutable。
// タプルが入るのであれば、public readonly (int X, int Y) Value; でいいはず。
// Point p に対して (var x, var y) = p; で、(var x, var y) = p.Value; の意味に解釈するのもよさそう。
public readonly PointValue Value;
// 外から mutable な型で値をもらう。いわゆる「ビルダー パターン」。
// PointValue をタプルにするのなら、スプラッティングが効いて、new Point(x, y) とも書けるはず。
// 簡易構文として、new Point { X = 1, Y = 2 } で、new Point(new PointValue { X = 1, Y = 2 }) の意味に解釈するのもよさそう。
public Point(PointValue value) { Value = value; }
// Value に処理を丸投げ
public int X => Value.X;
public int Y => Value.Y;
public override int GetHashCode() => Value.GetHashCode();
public override bool Equals(object obj) => (obj as Point)?.Value.Equals(Value) ?? false;
// 値書き換え(immutable なので別のインスタンスを作る)用の "wither" (GetX, SetX みたいなのを getter/setter というように、WithX をこう言う)
// p.Value { X = 1 } みたいに、new するとき以外でもオブジェクト初期化子を使えると、wither 書くのが楽になるはず。
// Point WithX(int x) => new Point(Value { X = x });
// あと、var q = p { X = 1 }; みたいなので new Point(Value { X = 1 }) の意味に解釈するのもよさそう。
// これなら、自前の wither 定義要らない。
public Point WithX(int x) { var v = Value; v.X = x; return new Point(v); }
public Point WithY(int y) { var v = Value; v.Y = y; return new Point(v); }
}

要点は

  • 値セマンティクスを持つクラスは、文字通り Value 1つだけを持つ
  • Value 用の構造体を作って、それを受け取るコンストラクターを作ることで、いわゆる「ビルダー パターン」になる
  • Value はタプル型を使って定義するとよさそう
  • オブジェクト初期化子の拡張で、「wither」も簡単に書けそう

これなら確かにタプルとレコードの親和性がよく、使う側のわかりやすさ的にもコンパイラーの実装負担的にも都合がよさそう。

まあ、これはこれで、元々のレコード型の提案にあった、部分的なメンバーの置き替え(以下のようなもの)の実装が難しくなりそう。


class Person(int Id, string Name)
{
public string Name { get; set; } // Name だけ mutable
}

レコードもタプルも、まだ提案の初期段階でこれから変わっていくでしょうし今後どうなるかはわかりませんが、これはこれで面白い検討内容でした。

Written by ufcpp

2015年2月11日 at 15:35

カテゴリー: C#

Tagged with