Quantcast
Channel: ++C++; // 未確認飛行 C ブログ
Viewing all 482 articles
Browse latest View live

TryRoslyn 改め SharpLab で、JIT Asm表示

$
0
0

いつの間にかと話題に。 去年の年末にちょこっと紹介したTryRoslynがJIT ASM (JITの結果がどういうネイティブコードになっているかを見れる)機能に対応してたみたいです。

TryRoslyn、今はSharpLabっていう名前に変わって、ドメインも取った見たいです:

ネイティブ コードの確認

例えばこんな感じのコードを書いて、

using static System.Math;

public class C
{
    public double M(double x, double y) => Exp(x) * Sin(y);
}

SharpLabに貼り付けて、 ページ内の「Decompiled」のコンボボックスで「JIT Asm」を選ぶと、以下のような結果が出ます (現時点での結果)。

; This is an experimental implementation.
; Please report any bugs to https://github.com/ashmind/TryRoslyn/issues.

; Desktop CLR v4.6.1590.00 (clr.dll) on x86.

C..ctor()
    L0000: ret

C.M(Double, Double)
    L0000: fld qword [esp+0xc]
    L0004: sub esp, 0x8
    L0007: fstp qword [esp]
    L000a: call System.Math.Exp(Double)
    L000f: fld qword [esp+0x4]
    L0013: fsin
    L0015: fmulp st1, st0
    L0017: ret 0x10

C#は、C# → IL にコンパイルする時点ではそこまで大きな最適化はしていなくて、 インライン展開とかの処理はIL → ネイティブコードに JIT コンパイルするタイミングで行っていたりします。 なので、本気で最適化に取り組みたかったらネイティブコードまで追わないと細かいところがわからなかったりします。

昔、Visual Studio上でネイティブコードまで追う方法とかも書きましたけど、 そこそこ面倒な手順だったりしますし。 さらっとオンラインで確認して見れるのは結構ありがたいかも。

中身

なんか流れを見るに、

という感じみたい。

ということで、最終的には.NETチームが作っているツールに行きつくみたいです。


ピックアップRoslyn 5/30: C# 7.1など(de:code 2017)

$
0
0

先週、de:code 2017で登壇してきたわけですが。 資料、公開しました。

トラック オーナーの方に「日本の第一人者」とかいう煽りタイトルを付けられてしまったわけですが。 なんかネタなタイトル(このすば)を冗談で言ってみたら採用されてしまい、「MVPはどいつもこいつも…」とか思われてそうで怖いわけですが。 ネタはタイトルだけです。

C# 7.1

で、本題。 de:code参加者の中には気づいた方もいらっしゃったみたいですけども、 この資料、de:code参加者向けの事前配布版と、当日発表でちょっと内容が変わっています。

変わったというか、C# 7.1のところのボリュームが単純に減ってるんですが。

つい最近、C# 7.1候補マイルストーンに並んでいる項目がごそっと減りました。 それを反映して、de:code登壇資料も削ったという感じです。 候補として残っているのは以下の4つ。

要は、リリースまでの時間が短いなら、リリースする機能は減ってしかるべき。 項目が減ったということは、早めのタイミングでのリリースが決まった。 ということかなぁと思われます。

先日プレビュー版が出た Visual Studio 2017 Update 3に、 C# 7.1も一部実装が載ったわけですが。 上記リリース タイミングの話もあり、 おそらくはVisual Studio 2017 Update 3と同時にC# 7.1もリリースなんじゃないかという状況です。

.NET Confが今年は7月19~21日にやるみたいなので、この辺りでの動きを期待したいところ。

Visual Studio 2017 Update 3 Preview 2

$
0
0

なんかVisual Studio上の通知に来てたので気づいたんですが、Visual Studio 2017 Update 3 の更新が来てました。 Visual Studio 2017 Update 3 Preview 2。 (なんかこう、もうVisual Studioもセマンティックバージョンにすればいいのに。15.3.2 とか。)

C# 7.1

で、ちょっと試した感じ、C# 7.1 予定の機能は全部入ってるっぽいです。 といっても、先日書いた通り、 入る機能はだいぶ絞ったっぽく、4つだけ。 以下のようなコードがちゃんとコンパイルできました。

.NET Fringe Japan 2016で登壇してきました

$
0
0

.NET Fringe Japan 2016やりました。

.NET Fringeっていう本家イベント(アメリカのポートランドで開催)があって、本家の人からそれの日本版をやりませんか?という話が来てそれに応じた形での開催だったりします。

アメリカのイベントって朝からがっつりやるんですよね。本家.NET Fringeは9時開始。日本でもたまにはがっつりやるかって感じで10時開始になりました。 まあ、逆に、アメリカのイベントは17時前に終わるんですが…10時~20時半はさすがにきつかったかなぁ。という感じも。

C#言語機能の作り方

で、僕のセッション。

作り方とかいいつつ、むしろ全力で止めにかかってる予防接種セッション。

⚠使用上の注意

  • 「独自に言語を作ろう・拡張しよう」みたいな話はハシカみたいなもんです
    • 早めに免疫つけといた方がいい
    • 歳食ってから罹ると重篤なことが

という。

とはいえ、やるだけ無駄とかやっちゃダメって話ではなく。むしろ、早めに罹っとけって話です。予防接種も、弱毒化したウィルスにあらかじめ罹ることで免疫付ける手法ですしね。

良し悪しとかどのくらい大変かとかはある程度の勘所がないと、いざ責任ある立場になって大きなことやれるようになった時に下手打つと命に係わるぞと。 この辺りは別にプログラミング言語作りに限らず一般論ですね。 いろいろ試しにやってみて、いろいろ勘をつかんでおくのは大事です。

しいて言うなら、なんかプログラミング言語作りでは、この「どのくらい大変か」を過少見積もりして重篤化する人がなんか一定割合いるっぽく、予防接種スライド作っとくか、と。 わざわざ大変なものに手を出さなくても罹った気分になっておくためのものが、僕が今回やったようなセッションですかね。

「ハシカみたいなもんだから」ってセリフ、いろんな方面に結構ぐさぐさ刺さったみたいですがw @kekyo さん、「新しい opcode 作るよ」って話(スライドニコ生放送の8時間目くらいから)をしてて「ハシカですね…」って言ってましたけども、 これは割かし良い具合にワクチン(弱毒化したウィルス)なんじゃないですかね。 最小限に手を入れてみるってのは勘所つかむためのかなり良い題材。 まあ、セッションの後、「予防接種ってのはそれなりに流行る可能性のあるものだから必要なんであって、これはハシカと言えるのか(そんなにやりたがる人いるのか)」とかいう鋭い突っ込みもあったんですが。

あと、試しにやってみているだけでも、@neueccさん(スライドニコ生放送の7時間目くらいから)が言ってたみたいに、公開するつもりでやる/実際に公開する方が身につく度合いが高いと思います。 まず、人に見せる/人に見られることを意識した方が断然勉強になります。 それにどうせお試しで実用性皆無なものであっても、公開していると「予防接種済み」の査証になりますし。結構価値あるんじゃないかと。

ピックアップ Roslyn 9/9

$
0
0

タプルの分解の最適化

タプル構築と同時に分解(要するに多値代入というか)する場合は、タプルを作らない最適化かけたいって。

要するに、例えば以下みたいなswapコード書いたとして、

var x = 1;
var y = 2;
(x, y) = (y, x);

今だとこうなる。

var x = 1;
var y = 2;
var v = new ValueTuple<int, int>(x, y);
x = v.Item1;
y = v.Item2;

これ、実のところこの時点でタプルの特別扱いが掛かってます。 分解の仕様上は、以下のようなコードになるべきところを、タプルでまでそれをやるのは無駄だってことで、Item1Item2の直参照に。

var x = 1;
var y = 2;
var v = new ValueTuple<int, int>(x, y);
v.Deconstruct(out x, out y);

Deconstructを最適化で消すんだったら、new ValueTupleの方も消していいんじゃない?という感じ。 なのでたぶん、以下のような感じのコードに展開されるのではないかと。 ほぼ、普通のswapコードに。

var x = 1;
var y = 2;
var tempX = x;
var tempY = y;
x = tempY;
y = tempX;

Recap of async streams

Lucian (C#チーム非同期担当の開発者)が夏休みから帰ってきて、現状のまとめを投稿。

まあ、本当にまとめのみ。基本、過去にブログで取り上げた内容なので詳細割愛。

あと、Stephen (.NETチーム内の人。活動を見るに.NETのパフォーマンス改善系の作業をしてるっぽい)がパフォーマンスがらみの懸念を投稿。

現状のIEnumerableの問題でもあるものの、MoveNextCurrentにメソッドが分かれてると、スレッド安全に作りようがなくて困る。 IAsyncEnumerableも同じような構造で作られると、async streamをチャネル的に(Go言語のgoroutine/channelみたいに)使いにくい/使えないので、スレッド安全にできる実装を考えてほしいとのこと。

ピックアップ Roslyn 6/30

$
0
0

csharplangに出てる提案の整理をしたみたいで、 いくつかの提案に「Proposal Champion」ラベルが付き始めたようです。

「Proposal Champion」は、Proposal(提案が出てるだけ)とChanpion(C#チームの誰かがオーナーになって進めることが決まった段階)の間くらいのつもりっぽく。 「いつかは取り組むけども、今すぐとはいかない」くらいみたいです。

以下、いくつか紹介。

末尾以外で名前付き引数

末尾以外の場所にある引数でも名前付き引数を使いたいという話。

どうもこれはプロトタイプ実装がすでに始まってるっぽい。

static void Main()
{
    // よく言われる話で、bool なフラグがたくさん並ぶとどれがどれかわからない
    M(true, true, true);

    // 名前付き引数には「オプションな引数を省略可能にする」という意味もあるけども
    M(isC: true);

    // どのフラグが何だったかを明記する意味でも使う
    M(isA: true, isB: true, isC: true);

    // (これまで) 末尾の引数だけを名前付きにすることならできた
    M(true, true, isC: true);

    // (提案) それ以外の位置でも、一部分だけ名前付きにできるようにしたい
    M(isA: true, true, true);
}

private static void M(bool isA = false, bool isB = false, bool isC = false)
{
}

どこででも拡張メソッドを定義

これまでだと拡張メソッドは、トップレベル(名前空間直下)の静的クラスの中でしか定義できませんでした。

でも、「あるクラスの中でだけ使う拡張メソッドのために、外に1クラス作るのは嫌」というような話はたびたび出てくるわけで、 「静的でないクラスでも拡張メソッドを定義したい」とか、 「入れ子のクラス内で拡張メソッドを定義したい」とかいう要望はよく見ます。

ということで、制限を緩めようという話がついに検討に上がったみたいです。

0b, 0x の直後に _ 区切り

C# 7で、数値リテラルの数字と数字の間を _ で区切れるようになりました (数字区切り文字)。

で、数字区切り(digit separator)の名前通り、ほんとに数字と数字の間にしか _ を書けません。

var a = 1_2_3; // OK
var b = 0b1111_1111; // OK
var c = 0b_1111_1111; // エラー。0b の直後に _ を書くのはダメ
var d = 0xab_cd; // OK
var e = 0x_ab_cd; // エラー。0x の直後に _ を書くのはダメ

元々、0b0x の直後にも _ を書きたいという要望は多々あったんですが、 「名前通りにしたい」、「数字区切りなのに数字でないところで区切れるようにはあんまりしたくない」みたいな雰囲気で、 C# 7ではこういう仕様になっています。

まあでも、やっぱり要望が大きく、0b0x の直後の _ (上記サンプルのceみたいな書き方)も認めようという流れになりつつあるみたいです。

分解にタプルは要らないのではないか

「提案」ってわけではないんですが。

C# 7で分解構文(deconstruction)が入りましたが、例えば以下のような場合を考えてみます。

class Point
{
    public int X { get; }
    public int Y { get; }
    public Point(int x, int y) => (X, Y) = (x, y);
    public void Deconstruct(out int x, out int y) => (x, y) = (X, Y);
}

class Program
{
    static void Main()
    {
        var (x, y) = (1, 2);

        // ↑現状は一度 ValueTuple を作ってから、それぞれ x, yに代入するようなコードに展開される
        //var t = new ValueTuple<int, int>(1, 2);
        //var x = t.Item1;
        //var y = t.Item2;

        // でも、仕様上は以下のような状態に展開する最適化を認めてるし、実際将来的にそういうコード生成する可能性がある
        //var x = 1;
        //var y = 2;

        // だったら、var (x, y) = (1, 2); に ValueTuple は不要なはず

        var (a, b) = new Point(1, 2);

        // ↑この場合は完全にタプルは要らないはず
        // 以下のようなコードに展開される(どこにも ValueTuple は出てこない)
        //var p = new Point(1, 2);
        //int a, b;
        //p.Deconstruct(out a, out b);

        // にもかかわらず、現状、分解を使っただけで ValueTuple (.NET 4.7以上、もしくは、System.ValueTuple パッケージの参照)が求められる
    }
}

現状だと、不要なはずの ValueTuple が求められます。

これは、将来的に以下のような構文を認めるために、分解とタプル構築の内部表現を統一したのの余波です。

var t = (var x, var y) = (1, 2);

// ↑これは、↓これと同じ意味
// (var x, var y) = (1, 2);
// var t = (x, y);

ですが、まあ、利用者としては不要なはずのパッケージ参照が必要になるというのは気分がいいものではありません。 実際「バグ報告」扱いで報告が入ってしまっている状況。

で、まあ、修正するようです。 本当に ValueTuple が必要になるまでは、パッケージ参照を求めないよう修正済み。 C# 7.2辺りに入れる予定みたいです。

ピックアップRoslyn 7/30: C# 7.1の先の計画& .NET でのメモリ手動管理

$
0
0

C# 7.1の先の計画

C# 7.1 がらみの作業も落ち着いたところで、 その先の優先度付け的な Design Notes がアップロードされていました。

概ね以下のような感じ。

  • 構造体の活用(特に ref がらみ)は C# 7.2 でまとめて実装
  • Utf8String は C# 8.0 に回りそう
  • static delegate とかいう話が出てる(C# 8.0向け)
    • static なメソッドは関数ポインターで直接管理したいという話
    • static なメソッドしか参照しないデリゲートのために Delegate 型インスタンス(ヒープを使う)を作りたくない
  • その他、バグっぽい挙動に関して
    • 自動実装プロパティで、バッキングフィールドに対して属性を適用できなかった問題(いつでも修正する用意あり)
    • long tuple (System.ValueTuple型の arity を超える8要素以上のタプル)に対するオーバーロード解決が怪しい問題(今のままにしたい)

.NET でのメモリ手動管理

これは .NET チームとか C# チームでの動きじゃなくて、マイクロソフト リサーチによる論文なんですが、 以下のようなものが出ています。

用途を絞らない汎用のメモリ利用に関してはガベージ コレクション(GC)の生産性を下げてまで手動管理を頑張ってもそれほど効果はないんですが、 一方で、手動管理が割かししやすくて、かつ、手動管理できれば数倍程度の高速化が見込めるような状況もあります。 そこで上記論文は .NET における手動メモリ管理に関する研究なんですが、以下のような内容。

  • GC と共存
    • 手動管理下にあるオブジェクトが、GC 管理下のオブジェクトの参照を持てる
    • GC に追加の負担を掛けない
  • マルチスレッド環境でも高速
    • lock なしでスレッド安全な手動メモリ管理を行う手法の導入
    • メモリの「owner」(メモリ解放の責任を負っていているただ1つのオブジェクト)と「shield」(一時的にメモリ参照権限を借り受けるオブジェクト)を用意して管理
  • (C# とかの)プログラミング言語との連携
    • 言語レベルで変数の「コピー不能性」を保証してもらえれば、低負担なメモリ管理アルゴリズムが使える

メモリ管理のアルゴリズムについては参考文献をいろいろ引用していて、 Rust とかでもやっている手法っぽい感じ。 GC との共存ができるという点と、実際に CoreCLRをベースに実装してみて、 計測してみたというあたりがこの論文のポイントになりそう。

owner (所有者。解放責任者)がはっきりしないとこの手法は使えないんで、用途は限られるんですが、 例えば以下のような用途に使えます。

  • List<T> とかの内部で要素の保存に使っている配列
  • キャッシュ管理
  • LINQ の GroupBy とかの中で使う一時的なハッシュテーブル

はまる用途に関しては2倍とか3倍とかの性能改善が期待できるという計測結果も出ています。

まあ、あくまでリサーチの段階なので、 実際に .NET に取り込まれるとしても結構先になる感じはあります。

Roslynに提案issueを立てた話: nullの扱いに関して

$
0
0

MVP Global Summit (グロサミ)に行ってきてたわけですが。

なんか、行きの飛行機内で思いついてしまって、そのまま向こうで頑張って issue 投稿、 せっかくだからグロサミ中に Mads (C# のPM)を捕まえて「こんな問題見つけちゃって、昨日ちょうどissue立てたんだけどどうしよう?」みたいな話を振ってきたり。

(先に具体例を書いておけば、どれだけ英語がつたなくても結構意図は伝えられる。)

(ちなみに、元々は帰国後にゆっくりページ書くつもりだったんだけど、なんかグロサミ参加の妙なテンションに任せて、 kekyoさん藤原さん室星さんとかと部屋の飲みの最中にこの方々も巻き込んでissue書きました。)

勢いで立てたものなので整理できてないんですが… たぶん2パートに分けた方が良さそう。 以下、改めて要約した内容(英語に訳して再投稿予定)。

2つの提案

nullの取り扱いと関連して、以下の2つを提案する。

  • 非default値型: default(T)の状態を認めない値型が必要ではないか
  • nullable-like型: 参照型とNullable<T>以外にも、?.??が使える型をユーザー定義できるようにすべきではないか

非default値型

Method Contracts、特に参照型の非null保証を入れようと思うと、確実な初期化処理が必須になる。

しかし、構造体の場合、defautl(T)など、既定値によって初期化処理を通らない「0/nullクリア」が発生する。 既定値の「0/nullクリア」のせいで、Contractsや非null保証のフロー解析が狂う可能性がある。

例1: 非null保証

パフォーマンスのために、参照型を1つだけ持つようなラッパーを構造体で作ることがある。 (例: ImmutableArray、) 今、パフォーマンスはC#の1つの大きなテーマであり、こういうケースは今後より一層増えるだろう。

例として以下のような構造体を考える。

struct Wrapper<T> where T : class
{
    public T Value { get; }
    public Wrapper(T value)
    {
        Value = value ?? throw new ArgumentNullException(nameof(value));
    }
}

レコード型非null保証が入れば、単に以下のように書けるだろう。

struct Wrapper<T>(T Value) whereT : class

単にTと書けば非nullとなり、nullを受け付けたければT?と書くようになる。 問題は、この構造体をdefault(Wrapper<T>)で作ると、T Value (本来は非nullであるはず)がnullになってしまうことである。

例2: 値の制約付きの構造体

以下のような、値に制約の入った構造体を考える。この例は、正の数しか受け取れない数値型である。

struct PositiveInt
{
    public int Value { get; }
    public PositiveInt(int value)
    {
        if (value <= 0) throw new ArgumentOutOfRangeException(nameof(value));
        Value = value;
    }
}

C#にレコード型Method Contractsが入ると、この構造体はおそらく以下のように書ける。

struct PositiveInt(int Value) requires Value > 0;

これでValueプロパティが常に0より大きいことが保証できているように見えるが、default(PositiveInt)のせいで、Valueに0が入ることがあり得る。この値は無効なはずである。

提案: 非defaultフロー解析

現在提案されている参照型の非null保証は、フロー解析に基づいている。 値型が既定値でないことも、同じフロー解析で行えるはずである。

そこで、non-nullable reference typesに対して、non-defaultable value typesを提案したい。 何らかのアノテーション、例えば[NonDefault]属性を付けた構造体は既定値を取ってはいけないとするのはどうだろうか。

[NonDefault]
struct Wrapper<T>(T Value) whereT : class

[NonDefault]
struct PositiveInt(int Value) requires Value > 0;

このとき、non-nullable reference typesに倣って、以下のように警告を出す。

PositiveInt x = default(PositiveInt); // warning
PositiveInt? y = default(PositiveInt); // OK
PositiveInt z = y; // warning
PositiveInt w = y ?? new PositiveInt(1); // OK

non-defaultable value typesに対するT?Nullable<T>を必要としない。 何故なら、default(T)は無効であり、x.HasValueを確かめなくても、x == default(T)で値を持っていないことが確認できるからである。 non-defaultable value typesに対してはnulldefault(T)と同一視してもいいかもしれない。

また、通常の構造体の中では、non-nullable reference typesのメンバーを持てないようにすべきだろう。 non-nullable reference typesを持てるのは、参照型か、non-defaultable value typesのみである。

nullable-like型

現在、参照型とNullable<T>構造体はC#コンパイラーによって特別な地位を与えられている。 すなわち、null条件演算子(?.)とnull合体演算子(??)の利用である。

しかし、これらの型以外にも、無効なインスタンスを?.??で伝搬/差し替えしたいことがある。

この挙動はmonad的であり、LINQTask-likeを使って無理やり解決しようとしている例もある。 しかし、悪用・乱用の類であり、決して読みやすいコードにはならないだろう。 無効なインスタンスの伝搬/差し替えは、やはり、?.??を使うべきである。

例1: UnityEngine.Object

Unityのゲーム中のオブジェクトの共通基底クラスとなるUnityEngine.Objectoperator ==をオーバーロードしていて、オブジェクトが持っているネイティブ リソースがすでに破棄されているとき、オブジェクトをnull扱いする(x == nullが真になる)。

しかし、参照型に対する?.??では、オーバーロードした==は呼ばれない(brtrue命令によるnullチェックに展開される)。 そのため、以下のように、?.を使ったコードが正しく動かない。

int? X(UnityEngine.Object obj)
{
    // OK
    if (obj == null) return null;
    return obj.GetInstanceID();
}

// runtime exception
int? Y(UnityEngine.Object obj) => obj?.GetInstanceID();

これまではUnityがC# 3.0にしか対応していなかったので問題にならなかったが、 Unity 5.5でC# 6.0に対応しようとしている。 この?.の挙動がはまるポイントになるだろう。

例2: Expected

無効な値としてnullを使うことを嫌う人が一定数いるが、その理由の1つが、「なぜ無効な値を返す必要があったのか」という原因に関する情報が消えることである。 そのため、Nullable<T>の代わりに、Tと例外のunion型にあたるExpected<T>のような型を作って使おうとする人がいる。 例えば、C++でそういう動きがみられる

struct Expected<T>
{
    public T Value { get; }
    public Exception Exception { get; }
}

もしC#でもそういう型を作るのであれば、?.を使った例外の伝搬や、??を使った例外からの復帰があってもいいのではないだろうか。

Expected<string> x = new Expected<string>(new Exception());
Expected<int> y = x?.Length;
string z = x ?? "";

提案: ユーザー定義のnullable-like型

所定のパターンを実装した型であれば?.および??を使えるようにすることを提案する。 Task-likeに倣ってこれをnullable-likeと呼ぼう。

例えば、以下のようなパターンはどうだろうか。

struct NullableLike<T>
{
    public T Value { get; }
    public bool HasValue { get; }
    // propagate a valid value
    public NullableLike<U> Propagate<U>(U value);
    // propagate an invalid value
    public NullableLike<T> Propagate();
}

これで、先ほどのExpected<T>の例であれば、以下のように展開する。

Expected<string> x = new Expected<string>(new Exception());
Expected<int> y = x.HasValue ? x.Propagate(x.Value.Length) : x.Propagate<int>();
string z = x.HasValue ? x.Value : "";

ちなみに、このパターンに沿ったExpected<T>の実装は以下のようになる。

struct Expected<T>
{
    public T Value { get; }
    public Exception Exception { get; }

    public Expected(T value)
    {
        Value = value;
        Exception = null;
    }
    public Expected(Exception exception)
    {
        Value = default(T);
        Exception = exception;
    }

    public bool HasValue => Exception == null;
    public Expected<U> Propagate<U>() => new Expected<U>(Exception);
    public Expected<U> Propagate<U>(U value) => new Expected<U>(value);
}

Visual Studio 2017 Update 3正式リリース

$
0
0

Visual Studio 2017 Update 3の正式リリースが来てるみたいですね。 普通にVisual Studio 2017上の「通知」や「拡張機能と更新」辺りからや、Visual Studio Installerから更新できます。

先週くらいから、中の人のブログとかtwitter周りで「リリース近い」的な言葉がちらほら出てきてた中、 一昨日、Preview 7.1とか出されたときにはちょっとひやひやしましたが (細かいリリース挟まないといけないような緊急のバグでも見つけてしまったの?と)。

なんかしかし、最近アップデートのたびに言ってますけども、2017 Update 3 Preview 7.1とか、某ZERO3↑↑とかハイパーIIアニバーサリースペシャルとかが可愛く見えるバージョン名はどうにかならないですかね…

いろいろ正式リリース

Visual Studio に伴って、

とかも正式リリースみたいです。

あと、Visual Studio for Mac version 7.1も同時に利用可能とのこと。

C# 7.1の機能自体は、6月のPreview 2の時点でfixしてたみたいで、その後全く変わっていません。 うちの「C# によるプログラミング入門」的にはだいぶ前から対応済み。 これくらい機能のふらつきが少ないと、記事書き的にはものっっすごい楽です。

C# 7.1を使う設定(プロパティ→ビルド→ビルドの詳細設定)

ただ、デフォルトで使われるC#のバージョンはC# 7.0で、7.1を使うには設定が必要みたいです。 この辺り、Previewの頃のまま。

プロジェクトのプロパティを開いて、ビルド→ビルドの詳細設定で変更可能。

  • 規定(default) オプションだと「最新のメジャー バージョン」= C# 7.0
  • 最新(latest)オプションだと「最新のマイナー バージョン」 = C# 7.1

になるみたいです。csproj に以下の行を無差別に追加してしまいたい。

  <PropertyGroup>
    <LangVersion>latest</LangVersion>
  </PropertyGroup>

.NET IDE の機能強化

今回、C# エディター上でのクイック アクションの類が結構いい感じに強化されてるんですが。 リリースノート上は

  • Resolve merge conflicts
  • Add null checks
  • Add parameter

くらいしか書かれてないんですよね… 他は「many more」扱い。

追記: https://docs.microsoft.com/ja-jp/visualstudio/ide/quick-actionsこの辺りにある程度は一覧があるっぽい。ただ、全部ではなさそうだし、Update 3での差分はわからなさそう。

個人的には他のクイック アクションの方がよく使ってるんで、ちょっとちゃんと調べて改めてまとめた方がいいかも。 (最近、C# の新機能以外は大体スルーしてたけど、久々に、IDE周りも真面目に。)

リリース ノート

そういえば、リリース ノートがちゃんと日本語でも即日来てますね。

最近、ドキュメントのページはあんまり翻訳を期待してなかったんですが、さすがに正式リリースくらいはちゃんとしますか…

それも割かし、機械翻訳っぽくないちゃんとした文面。

「Resolve merge conflicts」が「結合の競合の解決」になってたりとか、人手による翻訳でもやらかしそうなやつはきっちりやらかしてますが。 (ちなみに、Visual Studio上ではちゃんと「マージ競合」って翻訳されてました。↓みたいな機能。)

enter image description here

ピックアップRoslyn 9/3

$
0
0

今日は以下の3本立て:

  • 次のリリース(C# 7.2)の作業どのくらい進んでるのか久々に眺めてみた
  • 1個、Champion(取り組むこと自体は確定)判定を受けた提案が出てた(return, break, continue 式)
  • C# 8.0に向けて、「null許容参照型」の作業が最近活発化してる

C# 7.2(次のマイナーリリース)を試してみる

最近ちょっと本腰を入れてC#コードでの速度最適化の作業をしてたりするんですけども、 そこでたどり着いた1つの境地が「stackalloc 強すぎ」だったりします。

要するに、まあ .NET のヒープ確保は速いとは言われつつも、そもそもなくせるならなくす方が当然速い。 そのための手段として有効なのが構造体の活用と、このstackalloc。 ただ、まあ、unsafeコードだらけになります。

そのunsafeまみれを解消するのが C# 7.2 (次のマイナーリリース)のテーマだったりするわけです。 で、そういえば、「Span<T>を使えばsafeな文脈で stackalloc を使える」みたいな話を見かけたなぁとか思って、現状の実装状況を確認してみたりしました。

今、Visual Studio 2017 Previewでも提供されていないような新しい言語機能を試そうと思った場合、以下のようにすれば行けます。

  • Roslyn の nightly build から所望のバージョンのMicrosoft.Net.Compilersパッケージを取ってくる
    • 今回の場合は、2.6.0-rdonly-ref-62101-05ってバージョンを参照
    • この NuGet パッケージを参照していると、ビルド時に使われる C# コンパイラーが NuGet パッケージ中のものに刺し変わる
    • ちなみに、刺し変わるのはビルドに使われるもののみで、Visual StudioのC# エディター上でリアルタイムに動いているコンパイラーは刺し変わらないみたいです(ビルドは通るけどVisual Studio上ではエラーが出まくるみたいな状態になります)
  • csproj に<LangVersion>7.2</LangVersion> タグを追加

で試してみた結果が以下のような感じ。

このバージョンのC#コンパイラーは Roslynリポジトリのreadonly-refブランチが元になっている物っぽいんですが、 名前通りではなくて radonly-ref 以外の機能も含まれています。 含まれているのは以下のようなもの:

  • ref readonly参照引数を書き換え不能にできる機能
    • (昔は「readonly ref」の語順だったものの、今は「ref readonly」の順になっています)
    • in … reaf readonlyの省略形。out引数の対比として、「入力専用の参照引数」という意味で in というキーワードが利用できる
  • ref拡張メソッド … 「ref this」あるいは「in this」という書き方で、拡張メソッドの第1引数を参照渡しにできる
  • stackonly structSpan<T>構造体は参照戻り値・参照ローカル変数と同じ扱いをしないといろいろ不具合が起こるので、それをコンパイル時にチェックする
    • Span<T>がらみの機能の一環として、「Span<T>の変数で受けるならstackallocは安全」というものもある

この辺りはもう割と実装済みみたいでした。コンパイルも実行も可能。

return, break, continue 式

throw 式と同じ文脈で、return, break, continueも認めてもいいのではないかという話。(補足: throw 式が使える文脈ってのは、以下のように、=>とか??とか?:の後ろです。)

using System;

class Program
{
    static void Main()
    {
        Action a = () => throw new Exception(); // => の後ろ
    }

    static int F(int? n) => n ?? throw new Exception(); // ?? の後ろ
    static int F(bool b) => b ? 1 : throw new Exception(); // ?: の2、3項目

    static int F() => throw new Exception(); // => の後ろ
}

throw式の実装の時点でreturn式とかは要望がでていましたし、 6月くらいにはDesign Meetingの議題に挙がってたみたいですが、 この度、「Champion」(やること自体は確定)に昇格。 実装時期は未定(まだ分類されてない。タグ付けなし)。

null許容参照型

最近ついに、待望の「null許容参照型」がらみの作業が実働に入ったっぽい雰囲気。そのうちの1つが以下のissue投稿。

Mads (C# のプロダクトマネージャー)の null 許容参照型に関する現在の考えだそうです。 これまでと方針変更があったとかではなく、単に改めてまとめとして考えを表明しただけですが。

「nullがあるもの」だった参照型に対してわざわざ「null許容参照型」なんて呼んでいるのからわかる通り、 参照型も「何も修飾を付けなければnullはないもの」「?を付けたときだけnullを想定」扱いしたいという方針になっています。

もちろん単純にそれをやると破壊的変更なので、on/offは切り替えれるようにする。 切り替えはアセンブリ単位で、「このアセンブリnull許容参照型を前提としたコンパイル時検査を掛けています」みたいなアセンブリ属性も付けるみたい。

あと、null検査に関して、言語仕様と静的解析機能は分けたいみたい。

  • 言語機能としては、あくまで「nullを許すかどうかの注釈を付ける文法を用意」というだけ
    • 参照型Tに対して単にTと書くと非null、T?と書くとnull許容
    • 後置き!演算子を付けることでnull検査の対象から外す
      • パフォーマンスの都合で一時的にnullになるのをわかっていてやっている場合とか
      • 注釈が付いていない(C# 8.0以前に書かれていたり、解析オプションをoffにしていたり)アセンブリを参照する場合とか
  • それに対してどういう検査をするかは、概念上はC#コンパイラーの外、アナライザーとして実装する

ちなみに「概念上は」と言っているのは、C#コンパイラー内で実装しないと効率が悪い「解析」もあって実装上はC#コンパイラーに組み込むことになるから。とはいえ、「C#言語の仕様です」として厳密にいろいろと決めるには難しすぎる機能なので、「どこまで厳密に解析するかはアナライザーの実装に依存」としたいみたいです。解析の対象を段階的に広げていきたいみたいな思惑もあると思われます(「言語仕様」としてしまうと、後からの警告の追加も破壊的変更になるので)。

あと、「null検査」と言いつつ、C#としては、実際には「default検査」と表現するべきかも。 以下のようなものが静的解析の対象になります。

  1. 参照型に対する null の代入
  2. 参照型でインスタンス化される可能性がある型引数に対するdefaultの代入
  3. 参照型をフィールドに持つ構造体に対するdefaultの代入

これは「段階的に広げていきたい」の典型で、1は「必須」(at least)、2は「任意にできる」(could be optional)、3は「任意であるべきだろう」(should be optional)的に書かれていたり。

.NET Conf 2017 Tokyo, Japan

$
0
0

.NET Conf 2017 Tokyo, Japanやります。

  • 日時: 2017/10/07(土) (12:30開場、13:00開始)
  • 会場: 日本マイクロソフト株式会社 品川オフィスセミナールーム A, B, C+D

公募

今回の .NET Conf 2017 Tokyo では、8セッションを公募しています。.NET について話してみたいという方はこのチャンスにぜひ!

いつも決まったスピーカーの方ばかりで、喋りたくても自分にはなかなかチャンスがないと諦めているそこの貴方! この機会にぜひ応募ください。応募はコメント欄に「登壇興味あり」と記入するだけ( 追って運営側よりご連絡させていただきます)。 あるいは、後からでも、Connpassのメッセージその他手段は問わず運営にご連絡ください。

【セッション関連トピック】

  • .NET Core 2
  • EF Core 2
  • .NET Standard
  • ASP.NET Core (Razor Pages)
  • Xamarin / VS for Mac
  • Container / Microservices
  • Serverless + .NET
  • Language C# (F#, VB)
  • Unity/Game Development
  • IoT devices with .NET Core
  • Angular / SPA application with Azure
  • Modernizing applications (WinForms to UWP with Azure, WinForms / WPF to iPad App, etc...)
  • Cognitive, AI and Azure

Unconference + Unplugged部屋

公募の8セッションに加え、当日はさらに Unconference + Unplugged 専用部屋を準備します。

Unconferenceはテーマに沿って参加者が自由に喋るスタイルです。今回のテーマは.NETConfということで幅広く.NETに関連すればなんでもアリ。 だいたい一人10分程度目安で思いのたけをぶちまけましょう。奇麗なスライドを用意したり、かしこまって喋る必要はまったくありませんのでお気軽に参加ください。

UnpluggedはMicrosoftのScott Guthrieでもおなじみ、参加者からその場で質問を受けて回答していくスタイルで質問や疑問をぶつけて消化していくセッションです。

どちらも参加者と一緒にセッションを完成させるスタイルですので、質問などを考えながらお気軽に是非ご参加ください。

※ Unconference では、公募8セッションのセッション関連トピックを気にする必要はありません。テーマは自由です。こちらは当日喋りたい人が随時名乗りを上げて喋るスタイルですので、申し込み時に登壇興味あり等コメント記入する必要はありませんが、「Unconferenceで喋るかも」といったコメントがあると運営が喜びます

※ ちなみに、Unpluggedの1コマは僕、岩永がやろうかなと考えています。前々からちらほら「スライド等用意せず、C#がらみの話を何か来場者との対話ベースでやりたいなぁ」と言っていたやつを実際にやってみようかと。 (といっても、「じゃあ何か質問ある?」から始めてもあんまり手が挙がらないとは思うので、csharplangや僕の過去のセッション、記事、サンプルコード等をベースに何か定時しつつになるかなと思います。)

C#小ネタ集: C#をWeb上で試す

$
0
0

小ネタ

ここのところ、twitterで脊髄反射的に飛びついたきり、gistとかに書き散らかして終わりにしちゃってるネタがちらほら溜まってきたので、ここらでブログかしようかなと思います。 C#小ネタ集とか銘打って。

C#小ネタ集と言いつつ、あんまりC#が関係ない物もありますが。 結構溜まってるんで、数日は連続で書くことになるかなと思われます。

Web上で試したい

初日のネタは、C#をWeb上で触ってみようという話。

C#の入門記事とか書いてるとよく言われ続けていたのが、「Visual Studioをインストールするのがめんどくさい」って話です。 まあ、めんどくさいですよね。

そこで、今はC#にもWeb上でさらっと試すことができる場所があるので、今日はその紹介をします。

もちろん、じっくり腰を据えてコードを書くときにはもちろんVisual Studioを起動しといた方が楽なんですが。 (日々の仕事がC#とかだったりするとどうせ常時Visual Studioが起動してる状態にあるので別に立ち上げも大した面倒はありません。) とはいえ、入門向けにいきなり「Visual Studioを入れてください」は、今時ちょっとないよなぁ。

こういうのは、もうVisual Studioなどの統合開発環境に馴れてる人は忘れてる感覚です。 でも、自分が普段使わないプログラミング言語をちょっと試してみたいときのことを想像してみてください。 実務で使うわけでもなし、本腰入れてがっつり勉強するでもなし、ほんのちょっとしたネタを確認したいだけ。 そのためにわざわざ環境構築をしたいですかと言われると、まあ僕だったら嫌です。 実際、GoとかSwiftとかの挙動を見たいときは、まずWeb上で試します。 コピペ程度のコードしか書けませんが、試してみるだけならそれで十分なことが多いです。

C#をWeb上で

ということで、まずは公式サイト。

前にも1度ブログで取り上げていますが、 今は、公式サイト(dot.net!)で、試しにその場でC#を書いて実行結果が見れる機能が付いています。

dot.net内のStart codingコーナー

まあもちろん、paiza.ioとかideoneみたいなコード共有サイトを使ってもいいんですけども。 やっぱり公式サイトでやってないと、今時不格好ですよね… ということで、dot.netというドメイン取ったのと、その中でオンラインC#実行ができるようになってたことに気づいたときはうれしかったものです。

コード共有しつつ、C#をWeb上で

dot.netのオンライン エディターは保存してURLで他の人と共有したりはできません。

最近だと、適当に書き捨てのつもりで書いたコードも、 Gistとかに置いておいて、twitterとかで垂れ流したりもします。 そういう用途では、さすがに公式サイトのオンライン エディターは使えません。

そこで、ちょうどこれをやってくれるサイトがあります。 要するに、Gistと連携して、Gistに保存したC#コードを編集、実行してくれるサイト。 そういうサイトも作ってくている方がいらっしゃいます。

最新のC#をWeb上で

dot.net上にあるオンライン エディター中のC#は、「リリースされている最新版」です。 当然と言えば当然。 現時点ではC# 6ということになります。

dot.net内のStart codingはC# 6

GitHubリポジトリで開発途中の最新版を試したければ、まあ、通常、ソースコードをcloneしてきて、ビルドして、実行してみたり、そのデイリー ビルド結果を取ってきて、Visual Studio拡張などをインストールしてみる必要があります。

ですがここで、これも前にもブログで触れましたが、オンラインで割と最近のバージョンのC#を試せるサイトを作ってくれている人がいます。

RoslynのGitHubリポジトリからいろんなブランチを定期的に取ってきて、それぞれでどういう挙動をしているか確認できるサイトです。 実行結果というか、コンパイル結果のILとか、それを古いバージョンのC#で書くにはどうすればいいかという逆コンパイル結果を表示してくれます。

TryRoslyn

GitHub上のC#コードを解析

Visual Studioが常時起動しているとしても、 GitHubで見かけたC#コードを、わざわざCloneしてきて手元で見ようとはなかなか思わないですよね。 そのままブラウザーで読みたいです。

その一方で、GitHub上でコードを読んでいると、 クラス名などのシンタックス ハイライトがいまいちだったり、 「定義に移動」したくなるけど、当然GitHub上でそんなことできるはずなくイラッとしたりします。

ブラウザー中で、正しくハイライトされ、「定義に移動」とか「すべての参照を検索」ができるページがほしいです。

マイクロソフトが公式に提供している参照コードがちょうどそんな感じです。

これらのサイト、Roslynを使ったWebページ生成ツールで作ってるそうです。 ツール自体もオープンソースになっていて、以下のリポジトリで見れます。

で、要は、このツールを任意のGitHubリポジトリに対してかけてくれるサービスがあるとすごく幸せなわけですが… これがまた、そういうサービスを作ってくれてる大変親切な方がいらっしゃいます。

Visual Studio 2017 Release Candidate

$
0
0

来てた。

(Connect前のリーク騒動ありましたが… Connectをもって)

もう今、クロスプラットフォームを推す状況なので、Visual Studio for Mac (Xamarin Studioのリブランディング)と、モバイル開発、Azure辺りなんかが前面に出てますが。

とりあえず、「C#な人」的に言えることは、RCになって、C# 7として予定されている機能は全部載ったはず。うちのサイトのC# 7の新機能のページに書かれている内容は全部試せるようになっているはずです。

(ソースコード取ってきてビルドして最新の状況を試していたりすると、1か月前くらいからC# 7の全機能使えていたので、自分の中ではそこまでニュース性が大きくなかったりはするんですが。やっぱりインストーラーで入れれる製品で試せるようになると嬉しい。)

ちなみに… リポジトリのマイルストーンを覗いてしまうと、 この後RC 2, RC 3が控えていることが見えてしまっているわけですが。 RC 3かー… たぶんまだ結構変更入るんだろうなー… とはいえ、今回のやつでRC (リリース候補)の名前を冠してgo-live (自己責任でプロダクション環境で使ってOK)になりました。 使うか、仕事で…

ピックアップRoslyn 10/8: C# 7.1 の default 式のバグ

$
0
0

昨日の .NET Conf 2017 Tokyoでちょこっと話したりしたんですが、C# 7.1のバグを踏み抜いたという話。 (正確には同僚が踏んじゃったバグを僕がバグ報告入れた)

割と手短に示せるバグで、以下のような例がわかりやすいんですが、nullable型な引数のデフォルト値として、defaultを使ったとき、値が null にならないとおかしいはずなのに 0 になるという問題。

static void Main()
{
    int? x = default; // null
    int y = default; // 0

    Console.WriteLine(x); // null
    Console.WriteLine(y); // 0
    A(); // 0 !!
    A(default); // null !!
}

static void A(int? x = default) // 0 !!
{
    Console.WriteLine(x);
}

バグ報告

結構わかりやすいバグだから既出かなぁとか思いつつもバグ報告をいれてみたんですが。 案外初出だったらしく、かつ、さすがにまずそうなバグなので「破壊的変更になるけども直す」という感じですぐに修正作業が始まったみたいです。

一応、C# 7.1 (Visual Studio 15.3 (2017 Update 3))は、プレビュー版が出てからだと数か月経っていますし、正式リリース(8/14)からも2か月弱経ってるわけですが。気付かないもんですね、こういうバグ… まあ、デフォルト値が null の時には普通 null って書きますもんね、defaultを使わず。

ちなみに、類似のバグとして、default式はCode Fix の挙動も怪しかったりします。

IDE0034 Code Fix (変な挙動してる)

object x = default(int) (0になるはず)を、object x = default (null になっちゃうので、意味が変わる)にリファクタリングしようとしてしまう問題。

こちらは、既出のバグで先月くらいにはバグ報告されています。かつ、IDE のリファクタリングの問題なので、コンパイル結果自体が怪しい先ほどのバグと比べると幾分かマシ。

初のマイナーリリースで自信なさげ?

C# 7.1は、実質的には初のマイナーリリースですし、半年という短いサイクルで新バージョンをリリースしたというのも、C# チーム的には初の試みです。

そのせいか、ちょっと自信なさげな感じもあるんですよね…

例えば、C# チームから「C# 7.1 がリリースされました」的なブログが出ていなかったりしますし。

例えば、Visuals Studio 15.3では規定動作では C# 7.1 を使えませんし(LangVersion latest オプションの指定が必要)。

まあ、何事も初の試みは大変ってことですかね…

default の型推論はそんなの大変なのか

ちなみに、パッと見の感覚で言うと、このバグは不思議に思えるというか、なんでそんなバグを起こしちゃうのか謎というか。 「int? x = default って書かれてたら、ローカル変数だろうと引数のデフォルト値だろうとdefault(int?)だろ」という感じなんですが。

まあたぶんなんですけど、nullable型とか、値型のデフォルト値とかは結構いろんな特殊対応が入っていて、 思ったほどコンパイラーのコードは単純じゃないのではないかと思われます。

例えば、値型のデフォルト値なんですが、以下のような感じになっています。

// 以下の3つの X は全部同じ意味
static void X(DateTime d = default(DateTime)) { }
static void X(DateTime d = new DateTime()) { }

// これは現在の C# ではコンパイルできない
// C# 3.0 までデフォルト引数を認めていなかったので、VB との相互運用のために使っていた書き方
// 属性には定数しか含められず、かつ、構造体の規定値は .NET 的には「定数」ではない
// 代わりに null を入れてある
[DefaultParameterValue(null)]
static void X(DateTime d) { }

IL へのコンパイル結果としても以下のような感じ。

.method private hidebysig static
    void X(
        [opt] valuetype[mscorlib] System.DateTime d
    ) cil managed
{
    .param [1] = nullref // DateTime 構造体なのに null 扱い
    .maxstack 8
    IL_0000: ret
}

本来 null を代入できないはずの DateTime 構造体に対して、null でデフォルト値を代用している状態です。

nullable 型に対しても、以下のような感じで、null の扱いがちょっと特殊になっています。

// 以下の2行は同じ意味
int? x = null;
int? x = new int?(); // null というより、(false, 0) みたいな値

なんかこの辺りが合わさった結果、default式の型推論に失敗したんじゃないかなぁと思います。

修正による破壊的変更

このバグを修正すると破壊的変更になります。 もしも、「0 になるものだ」という前提で int? x = default を書いている人がすでにいた場合、 そのコードの挙動が狂います。

C# は基本的には破壊的変更に対して非常に保守的な言語で、 「直したいけども破壊的変更になるから直せない」みたいな言語仕様も結構あります。 ただ、「明らかにバグ」というものに対してはさすがに破壊的変更が許容されることが結構多いです。

一応、マイクロソフト内に「compat council」(互換性協議会)みたいなグループがあって、そこでの審議をしているそうです。 そのcouncilで破壊的変更を許容するかどうかは、バグの深刻度と実際の影響範囲のバランス等を見て決めていると思われます。

今回の場合で言うと、

  • int? x = default が 0 になるものだという前提でコードを書いているやつがどのくらいいるか? → あんまりいなさそう
    • そもそも数か月の間誰にも気づかれなかったわけで
    • 普通は int? x = nullint? x = 0 と書く(わざわざ int? x = default(int?) とか int? x = default(int) とか書かない)
    • LangVersion latest オプションが必要なため、そもそも C# 7.1 の利用率自体がまだそこまで上がっていないと思われる
  • いくらなんでも挙動がひどい
    • ローカル変数の int? x = default; だと null なのに、引数のデフォルト値の(int? x = default) だと 0
    • X() だと 0 なのに、X(default) だと null

という感じなので、 さすがにほぼノータイムでcompat councilを通過したみたい。

ピックアップRoslyn 10/9

$
0
0

今日は、新たにChampion (取り組むこと確定) が2件と、面白そうな提案1件。

ref ローカル変数の再代入

C# 7.0 で、参照ローカル変数が使えるようになっていましたが、 参照ローカル変数の再代入はできませんでした。

static ref int Max(int[] array)
{
    if (array.Length == 0) throw new ArgumentException();

    ref var max = ref array[0];

    for (var i = 0; i < array.Length; ++i)
    {
        if (max < array[i])
        {
            // max = x; だと、array[0] の内容を上書きしちゃうのでダメ
            // こう書きたい(C# 7.0 では無理)
            ref max = ref array[i];
        }
    }

    return ref max;
}

一方、C# 7.2でつかされる機能として前々から決まっていたものとして、ref-like 型というものがあります。 これまでの .NET では認められていなかった「フィールドとして参照を持てる構造体」を認めるための仕様です。 その手の構造体を安全に使うにはref (参照引数、参照ローカル変数、参照戻り値)と同じようなフロー解析が必要で、 その仕様が C# 7.2 で追加されます。

で、C# 7.2のref-like型では、変数への「参照の再代入」を認めている(認められるようにフロー解析を賢く実装した)ので、 だったら、C# 7.0までの参照ローカル変数でも再代入を認められるはず。 ということで、これもC# 7.2で実装しようという流れになっています。

宣言式

こちらは C# 6.0 の頃から提案に上がっていたもの。 以下のように、式の途中で変数宣言ができるという機能。

var square = (var x = int.Parse(Console.ReadLine()) * x;

「パターンマッチと併せて練り直したい」、「パターンマッチ同様、変数xのスコープをどうするかちょっと迷う」、「大変な割には需要は低め(やらないとは言わないけど優先度低)」みたいな状態だったものに、ついに「Chanpion」タグが付きました。

まあ、ただし、マイルストーンが決まっていないので相変わらず優先度低めです。

値型 enumerator

今の仕様だと、イテレーターを以下のように書きます。

static IEnumerable<int> X()
{
    yield return 1;
    yield return 2;
    yield return 3;
}

これで何が問題かというと、必ずインターフェイスを介して列挙子を返すことになるので、 ヒープ確保が避けれないという点です。

で、この問題を避けるために、結局、イテレーター構文は使わず、構造体な列挙子を1つ1つ作ったりするというつらい最適化作業が待っていたりします。 まあ昔からですが、List<T>GetEnumeratorなんかがそういう実装になっています。

書きやすさとパフォーマンスのトレードオフは常にあるものなのでしょうがないと言えばしょうがないんですが、 やっぱり最初から「構造体を生成してくれるイテレーターが欲しい」という提案が出てきたという状態。


Visual Studio 15.5 Preview

$
0
0

こないだ Visual Studio 2017 Version 15.4の正式リリースが出たところですが。 (主にUWP/Windows 10 Fall Creators Update絡みだったので個人的にはバグフィックス以外そこまで恩恵なし。)

翌日にもう[Visual Studio 2017 Version 15.5のプレビュー版]が。

「Stepping Back」デバッグ(1つ前のブレークポイントに状態を戻せる機能)とか割かし素敵そうな。

それはそれとして、15.5の告知ブログにはどこにも書かれていませんが、C# 7.2が含まれています。 大々的に書かない辺り、やっぱり自信ないのかな… まあ、こっそり出したところで、Roslynリポジトリを見ていれば15.5で出すことだいぶ前からわかっていますが。

C# 7.2

ということで、C# 7.2。

C# 7.2で追加予定となっている機能を一通り手元で試してみましたが、 「ref local reassignment」以外は実装されていそう。 (※追記: ref local reassignment はそもそも 7.3 に延期されてそう。なので、予定されている機能は全部実装済み。) あと、先日報告を出したバグの修正は、さすがに今回のバージョンには含まれていませんけども、 15.5の正式リリースまでには入ると思います。

試しに一通り書いてみたコードはうちのサンプル リポジトリに置いてあります。

C# 7.1の時と同様に最初は Gist 辺りに書き捨てとこうかと思ったんですが、思ったよりも分量が多く。 結構なコード量だったのでちゃんとソリューションを作って複数のファイルに分けて書くことになったので GitHub リポジトリにコミット。

ref

C# 7.2の当初予定だと「パフォーマンス関連の機能詰め込み」みたいな感じだったんですけど、やっぱ一部はもっと後に伸びました。 で、残ってるのが何かというと、もうほとんどがrefがらみ。

C# 7.0の参照戻り値・参照ローカル変数の延長にあたる機能です。

(※ readonly structs とかは refと関係なさそうにも見えますが、これがないと ref の安全性の保証ができないそうで。)

C# 7.0の参照戻り値の時点で、「9割方の人はおそらく使わない機能」、「残り1割(未満かも)の人が、ライブラリやフレームワークのパフォーマンス改善に使う」、「結果的に、全てのC#ユーザーがパフォーマンス改善の恩恵を受ける」的な機能なわけですが。 C# 7.2はもう、このバージョン全体がそんな感じ。

一応他にも新機能があるんですが、ものすっごい小粒です。

小ネタ 式の評価順序

$
0
0

C#小ネタと言いつつ、IL小ネタになりがちだったので、今日はC#小ネタらしく。

最初にちょっとしたクイズ。 まず、中身は何でもいいんですが適当な2引数のメソッドを用意します。 例として、単純な足し算でも用意しておきましょう。

static int F(int x, int y) => x + y;

以下の2つのコードの挙動は同じでしょうか?違うでしょうか?

1つ目: 一時変数を使用

var temp = F(2, 3);
var result = F(1, temp);

2つ目: 1つの式で計算

var result = F(1, F(2, 3));

まあ、同じですね。副作用を残さない限りは。

オペランドの評価順序

ということで、今日はオペランドの評価順の話です。 上記の2つのコードを、わざと副作用付きに書き換えてみます。

そのためにとりあえず、副作用を起こすメソッドを追加。 Consoleにログ出力した後、引数を素通しするだけのメソッドです。 値渡し版と参照渡し版を用意。

// WriteLine + 素通し
static T Log<T>(T x) { Console.WriteLine(x); return x; }
static ref T Log<T>(ref T x) { Console.WriteLine(x); return ref x; }

1つ目(一時変数を使用)を改めて:

var temp = F(Log(2), Log(3));
var result = F(Log(1), temp);
2
3
1

2つ目(1つの式で計算)を改めて:

var result = F(Log(1), F(Log(2), Log(3)));
1
2
3

C#では、式の評価は、上から下へ、左から右へ逐次実行です。 なので、一時変数を導入すると結果が変わります。

1つの式の中でも、演算子の優先順位や結合方向とは無関係に、評価は一律左から右です。 代入(優先度が低い上に右から左に結合)が混ざっていようと、そのオペランドの評価は左から右です。

例えば以下の通り。

bool x = false, y = true;
Log(ref x) = Log(ref y) = Log(1) + Log(2) * Log(3) > Log(4) & Log(5) <= Log(6) - Log(7) | Log(8) == Log(9);
False
True
1
2
3
4
5
6
7
8
9

名前付き引数のオペランド評価

前節の結果はそんなに不思議なことないでしょう。コードから挙動を予想しやすいって意味では書かれてる順番通りが一番です。それに、パフォーマンス的にも悪い選択ではありません。例えば、

var result = F(1, F(2, 3));

というようなコードであれば、コンパイル結果は以下のような感じになります(必要なところを抜粋)。

ldc.i4.1
ldc.i4.2
ldc.i4.3
call       F
call       F

元のC#のオペランドと同じ、1, 2, 3の順でldc (load constant)しています。 副作用を起こすためにLogメソッドを挟む場合、このldcのところが数命令に置き換わりますが、命令の並ぶ順序はこの場合と同じです。

ということで、素直に実装すればいいだけ…

でもなくて、まあ、ほとんどの式は素直に実装していいんですが、一部めんどくさい奴がいます。例えば、名前付き引数。 以下のようなコードを書いたとします。 x, yを逆に並べています。

F(1, F(y: 2, x: 3));

すると、コンパイル結果は以下の通り。C#ソースコード上は1, 2, 3だったものが、IL的には1, 3, 2になります。

ldc.i4.1
ldc.i4.3
ldc.i4.2
call       F
call       F

この結果は副作用がないからこそ、C#コンパイラーの最適化が掛かってこうなっています。 副作用がないことがわかっている場合、評価順を並べ替えを行います。

一方で、副作用があるとそうはいきません。あくまで、C#では左から右への評価が必要です。

例えば以下のようなコードをでは、ちゃんと、1, 2, 3の順での評価が必要です。

F(Log(1), F(y: Log(2), x: Log(3)));

コンパイル結果は以下の通りです。これまでは必要のなかった一時変数(stloc: ローカル変数へのストア)が必要になります。

ldc.i4.1
call       Log
ldc.i4.2
call       Log
stloc.0    // 一時変数!
ldc.i4.3
call       Log
ldloc.0
call       F
call       F

ちなみに、これ、C# 4.0の時にはバグってて評価順が狂ってた(逆順になってた)そうです。 C# 5.0でバグ修正した結果、破壊的変更になっていたり(めったにこんなコード書かない上に、バグの修正なので特に問題にはならず)。

タプルの要素の評価順序

もう1個変な例を挙げておきましょう。C# 7で導入されるタプルと分解で、以下のように、swapコードを書けるようになりました。

var x = 1;
var y = 2;
(x, y) = (y, x);
Console.WriteLine($"{x}, {y}");

これ、同じ処理をタプルを使わず書くとすると、まあ、以下のようにしますよね。

var temp = x;
x = y;
y = temp;

こいつらにも副作用を加えてみましょう。

まず、タプルを使うもの。

var x = 1;
var y = 2;
(Log(ref x), Log(ref y)) = (Log(y), Log(x));

ちゃんと、これも左から右に順に評価されます。すなわち、ref x, ref y, y, xの順。なので結果は以下の通り。

1
2
2
1

で、これをタプルなしで副作用も込みで全く同じ挙動にするためにはどうするか。

先ほどの類推で以下のように書いてしまうと、副作用の順序が変わります。

var temp = Log(x);
Log(ref x) = Log(y);
Log(ref y) = temp;
1
1
2
2

正しくは、以下のように書かないと同じにはなりません。

ref var rx = ref Log(ref x);
ref var ry = ref Log(ref y);
var vy = Log(y);
var vx = Log(x);
rx = vy;
ry = vx;

まとめ

副作用があっても常に一定の結果になるように、C#では、オペランドの評価順が常に左から右、書かれている通りの順序で行われます(まあ、割かし最近のプログラミング言語では大体同じで順序保証があります)。

ただ、順序保証がない場合に比べて、保証のためのコストがちょっとだけかかります(なので、古いプログラミング言語では「コンパイラーの実装ごとに変えていい」となっているものも結構あります)。

まあ、これだけ書いておいて身もふたもない結論で締めますが、副作用起こすような式を書くやつが悪い。

絵文字の連結と、書記素クラスター判定

$
0
0

みんな絵文字好きすぎだろ…

というRT状況なわけですが。

emoji zwj sequences

元々、👪 (U+1F46A)という、1文字で家族を表す絵文字があったわけですが。

「白人の絵しかないのはおかしい」とか「LGBT に配慮しろ。なぜ男女ペアしかないんだ」とかいろいろと地雷になってしまった結果、 合字で解決しようとかいう仕様が Unicode に入ってしまって今に至っているわけですが。

ちなみに、単に合字になるというだけじゃなくて、 「合字は1文字として扱え」という仕様も決まっています。 仕様は以下のページにあり。

まあ、仕様があるといってもそれにアプリが対応しているかどうかというとまちまち。 以下の動画でのChromeの動作みたいに、「ページ中ではちゃんと1文字として扱う」、「アドレスバーでは家族1人1人がバラバラに」という挙動になったり。

で、このAnnex #29の仕様では、 家族どころか、絵文字を ZWJ (zero width joiner: ゼロ幅接合子)で繋げば何人でもつながります。 これが、emoji zwj sequences というやつ。

そして、ZWJ でいくらでもつながるんですけど、それをグリフ的にどう描画すべきかというとまたこれが難しくて、その結果が冒頭のツイートの内容。

emoji zwj sequences

「合字は1文字として扱え」仕様 … 書記素判定

ちなみに、「グリフ的に1つにくっつくから、ユーザーから見ると1文字に見える」、 「なので、カーソル移動や削除の際には1文字として扱え」というものを書記素(grapheme)といいます。

Annex #29は「書記素の区切りはどこか」(と、単語の区切り、センテンスの区切り)を判定するためのルールを決めたものです。 先ほどの動画を見ての通り環境次第ではあるんですが、最近のOSは結構ちゃんとこの「書記素の区切り判定」を実装しています。

C# で書記素判定するライブラリ

でも、この辺りは割かし新しい仕様な上に、Unicode のバージョン依存が激しく、あんまり実装したい類の処理ではなく。 その結果、プログラミングに使えるライブラリとしてはあんまり提供されてなかったり。

標準ライブラリで書記素判定が提供されているものは少ないですし、 あっても「Unicode 8.0の仕様」、「家族絵文字や肌色には対応できない」とかだったりもします。

ということで、ないものは自分で作ったのがこちら。

書記素判定の C# 実装です。 一応、Unicode 10.0 相当。

Web とかゲームとかでこの手の文字数判定をやりたくなることもあるでしょうし。 .NET Framework 3.5 にも対応してあるので、もしなんなら Unity でもお使いいただけると思います。

おまけ

結構いやいやではあるんですけど。 どのくらい嫌かは、この2万行近い switch ステートメントを見てもらえれば伝わるかと思います…

要するに、どの文字がどういうカテゴリーなのか、1文字1文字テーブルを引く必要があって、 このテーブルがとにかく巨大です。 しかも Unicode のバージョン依存。

ちなみに、この2万行近い switch ステートメントは、unicode.org が提供しているテーブル データからコード生成で作っています。 C# コンパイラーが結構いい感じに最適化してくれるんで割かし高速なコードが生成されるんですけども、 その代わり、この1ファイルがあるだけでコンパイル時間がものすごい伸びます…

小ネタ 式の評価順序

$
0
0

C#小ネタと言いつつ、IL小ネタになりがちだったので、今日はC#小ネタらしく。

最初にちょっとしたクイズ。 まず、中身は何でもいいんですが適当な2引数のメソッドを用意します。 例として、単純な足し算でも用意しておきましょう。

static int F(int x, int y) => x + y;

以下の2つのコードの挙動は同じでしょうか?違うでしょうか?

1つ目: 一時変数を使用

var temp = F(2, 3);
var result = F(1, temp);

2つ目: 1つの式で計算

var result = F(1, F(2, 3));

まあ、同じですね。副作用を残さない限りは。

オペランドの評価順序

ということで、今日はオペランドの評価順の話です。 上記の2つのコードを、わざと副作用付きに書き換えてみます。

そのためにとりあえず、副作用を起こすメソッドを追加。 Consoleにログ出力した後、引数を素通しするだけのメソッドです。 値渡し版と参照渡し版を用意。

// WriteLine + 素通し
static T Log<T>(T x) { Console.WriteLine(x); return x; }
static ref T Log<T>(ref T x) { Console.WriteLine(x); return ref x; }

1つ目(一時変数を使用)を改めて:

var temp = F(Log(2), Log(3));
var result = F(Log(1), temp);
2
3
1

2つ目(1つの式で計算)を改めて:

var result = F(Log(1), F(Log(2), Log(3)));
1
2
3

C#では、式の評価は、上から下へ、左から右へ逐次実行です。 なので、一時変数を導入すると結果が変わります。

1つの式の中でも、演算子の優先順位や結合方向とは無関係に、評価は一律左から右です。 代入(優先度が低い上に右から左に結合)が混ざっていようと、そのオペランドの評価は左から右です。

例えば以下の通り。

bool x = false, y = true;
Log(ref x) = Log(ref y) = Log(1) + Log(2) * Log(3) > Log(4) & Log(5) <= Log(6) - Log(7) | Log(8) == Log(9);
False
True
1
2
3
4
5
6
7
8
9

名前付き引数のオペランド評価

前節の結果はそんなに不思議なことないでしょう。コードから挙動を予想しやすいって意味では書かれてる順番通りが一番です。それに、パフォーマンス的にも悪い選択ではありません。例えば、

var result = F(1, F(2, 3));

というようなコードであれば、コンパイル結果は以下のような感じになります(必要なところを抜粋)。

ldc.i4.1
ldc.i4.2
ldc.i4.3
call       F
call       F

元のC#のオペランドと同じ、1, 2, 3の順でldc (load constant)しています。 副作用を起こすためにLogメソッドを挟む場合、このldcのところが数命令に置き換わりますが、命令の並ぶ順序はこの場合と同じです。

ということで、素直に実装すればいいだけ…

でもなくて、まあ、ほとんどの式は素直に実装していいんですが、一部めんどくさい奴がいます。例えば、名前付き引数。 以下のようなコードを書いたとします。 x, yを逆に並べています。

F(1, F(y: 2, x: 3));

すると、コンパイル結果は以下の通り。C#ソースコード上は1, 2, 3だったものが、IL的には1, 3, 2になります。

ldc.i4.1
ldc.i4.3
ldc.i4.2
call       F
call       F

この結果は副作用がないからこそ、C#コンパイラーの最適化が掛かってこうなっています。 副作用がないことがわかっている場合、評価順を並べ替えを行います。

一方で、副作用があるとそうはいきません。あくまで、C#では左から右への評価が必要です。

例えば以下のようなコードをでは、ちゃんと、1, 2, 3の順での評価が必要です。

F(Log(1), F(y: Log(2), x: Log(3)));

コンパイル結果は以下の通りです。これまでは必要のなかった一時変数(stloc: ローカル変数へのストア)が必要になります。

ldc.i4.1
call       Log
ldc.i4.2
call       Log
stloc.0    // 一時変数!
ldc.i4.3
call       Log
ldloc.0
call       F
call       F

ちなみに、これ、C# 4.0の時にはバグってて評価順が狂ってた(逆順になってた)そうです。 C# 5.0でバグ修正した結果、破壊的変更になっていたり(めったにこんなコード書かない上に、バグの修正なので特に問題にはならず)。

タプルの要素の評価順序

もう1個変な例を挙げておきましょう。C# 7で導入されるタプルと分解で、以下のように、swapコードを書けるようになりました。

var x = 1;
var y = 2;
(x, y) = (y, x);
Console.WriteLine($"{x}, {y}");

これ、同じ処理をタプルを使わず書くとすると、まあ、以下のようにしますよね。

var temp = x;
x = y;
y = temp;

こいつらにも副作用を加えてみましょう。

まず、タプルを使うもの。

var x = 1;
var y = 2;
(Log(ref x), Log(ref y)) = (Log(y), Log(x));

ちゃんと、これも左から右に順に評価されます。すなわち、ref x, ref y, y, xの順。なので結果は以下の通り。

1
2
2
1

で、これをタプルなしで副作用も込みで全く同じ挙動にするためにはどうするか。

先ほどの類推で以下のように書いてしまうと、副作用の順序が変わります。

var temp = Log(x);
Log(ref x) = Log(y);
Log(ref y) = temp;
1
1
2
2

正しくは、以下のように書かないと同じにはなりません。

ref var rx = ref Log(ref x);
ref var ry = ref Log(ref y);
var vy = Log(y);
var vx = Log(x);
rx = vy;
ry = vx;

まとめ

副作用があっても常に一定の結果になるように、C#では、オペランドの評価順が常に左から右、書かれている通りの順序で行われます(まあ、割かし最近のプログラミング言語では大体同じで順序保証があります)。

ただ、順序保証がない場合に比べて、保証のためのコストがちょっとだけかかります(なので、古いプログラミング言語では「コンパイラーの実装ごとに変えていい」となっているものも結構あります)。

まあ、これだけ書いておいて身もふたもない結論で締めますが、副作用起こすような式を書くやつが悪い。

GraphemeSplitter の副産物(絵文字調査ツールとRangeスイッチ)

$
0
0

先週書いた GraphemeSplitter の話からの派生で2点。

常設の Web サイト化

GraphemeSplitter のサンプルのつもりで ASP.NET Razor Page なページを1個作ったんですけども。

その後。

  • なんか気になる絵文字を見かけた
  • 絵文字なのでそれの正確な意味わからないくて気になる
  • しょうがないからその絵文字でググったり
  • 文字コードを調べて U+1F680 でググったり(面倒)
  • そうだ、こないだ作ったサンプル用のページでコード調べられる
  • Visual Studio でわざわざデバッグ実行起動するのばかばかしくなってきた
  • そうだ、Azure の Free プランでホストしとこう
  • ついでだから便利機能(自分用)足しとこう(今ここ)

ということで、書記素分割サンプルの今

追加した機能は以下のようなもの。

  • 入力用のフォームを設置
  • 1コードポイントごとにばらした文字も表示
  • 文字コードに対して、「U+1F680」みたいなGoogle検索のURLのリンクを生成

書記素分割というか、単なる絵文字調査ツールになりました。 仕組み上、絵文字だけじゃなくて、ASCII顔文字の類も調べられます。

ソースコードは「自分用」と言わんばかりに UfcppSample リポジトリの Tools フォルダー以下に置いてたり。

Rangeベースのswitch-case

書記素分割のために、unicode.org 内の GraphemeBreakProperty ってデータを参照して、そこからコード生成でswitchステートメントを作ったわけですが。2万行弱のクソコードっぷりがひどく…

で、以下のような最適化を掛けてほしいというの、とりあえず issue 立てとくことに

// 1コード1 case に展開 (最適化が掛かって結構速いけど、クソコード。コンパイル時間もやたらと遅い)
switch (codePoint)
{
    case 1536:
    case 1537:
    case 1538:
    case 1539:
    case 1540:
    case 1541:
        // ...
}

// when 句を使って範囲を表現 (コードは綺麗になったけど、条件判定が線形探索になっちゃって O(n)。遅い)
// 二分探索するような最適化をしてほしい
switch (codePoint)
{
    case uint i0 when 1536 <= i0 && i0 <= 1541:
        // ...
}

// こういう文法ほしい(C# 8とか9とかくらいでは入るかなぁ、きっと)
// これなら、コードが綺麗、かつ、最適化しやすいのではないか
switch (codePoint)
{
    case 1536..1541:
        // ...
}

まあさすがに、実用途の説明と、ベンチマークを添えとくと反応よさげ。case 1536..1541 みたいな書き方であれば範囲の重複チェック(同じ値の case が複数あったらエラーにする)のとか、二分探索化する最適化もしやすそうということで、興味を持ってもらえたっぽい。

Viewing all 482 articles
Browse latest View live