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

C# 6, C# 7

$
0
0

最近、C#がらみの原稿仕事を依頼されたとき、よくある校正が「C# 6になってますけども、6.0ですよね?」。 回答は「いえ、C# 6が正式です」。

C#のバージョン番号、6以降は .0 を付けないのが正式っぽいんですよね。実は。

  • C# 1.0
  • C# 1.1 (#lineディレクティブと/** doc comment */が追加されただけ。1.1なのか1.2なのかも割かしよくわからず)
  • C# 2.0
  • C# 3.0
  • C# 4.0
  • C# 5.0
  • C# 6
  • C# 7

という感じ。

実質的に小数点以下使ってない(1.1とか存在感ない)し、要らないですよね。という感じ。

これに気づいたの、結構後になってからなんで、うちのC#によるプログラミング入門にも「C# 6.0」って表記が結構残ってたんですが、今しがた整理。ブログ以外からは6.0が消えたはず。

実はよくわからない

ただ、C# 7の次がC# 7.1とかになる可能性は0ではないです。「TypeScriptとか0.1刻みの細かいリリースしてるよね」みたいな話題もあるし、C#も1年1回リリースくらいのペースに速めようなんて話が出てますし。パターン マッチングの構文を、7で追加するものと、その先に回すものを分けたとかいうのもありますし。

.NET Frameworkも、「.NET 4」ってブランド名にしたら、その次からが「.NET 4.5」、「.NET 4.5.1」、「.NET 4.6」、…とかだったなんて話もあります。

あと、C#チームは現体制(GitHub上で公開MadsがPM)になってから、この手のアナウンスが手抜きなんですよね。気が付いたら「あれ?そうだったの?」みたいなことが割とちらほら。

単に、「6.0と6に何の差もないだろう」的な発想なのかも。例えば以下のページなんて、C# 1, C# 2, C# 3, ...表記。

ってことで、自分も全部C# 1, C# 2, C# 3, ...表記に変えたい気持ちありつつ。まあ、一応過去への遡及はしないことにします… 6以降だけ、.0を取る方針で。

おまけ: VB

Visual Basicのバージョンもなかなかよくわからないみたいですね。

C#は、言語のバージョンとIDEのバージョンを分けて表記してますけど、「Visual Basic」っていうIDE製品があった頃の名残で、言語とIDEの区別をしないみたい。その結果何が起こるかというと…

まあ、以下のページを見てください。

書かれてるのは以下の通り。

  • Visual Basic / Visual Studio .NET 2002 (内部バージョン7)
  • Visual Basic / Visual Studio .NET 2003 (7.1)
  • Visual Basic / Visual Studio .NET 2005 (8)
  • Visual Basic / Visual Studio .NET 2008 (9)
  • Visual Basic / Visual Studio .NET 2010 (10)
  • Visual Basic / Visual Studio .NET 2012 (11)
  • Visual Basic / Visual Studio .NET 2013 (12)
    • このとき、コンパイラーは全く更新してません
  • Visual Basic / Visual Studio .NET 2015 (14)
    • 13を嫌って、内部バージョン1つ飛ばして14

で、VBのバージョンとしては、Visual Studioの内部バージョンで(2002みたいな年号表記じゃなくて、7みたいなの)表します。 問題は、2013。VBのコンパイラーは一切変更してないのに、こいつのことをVB 12って言うみたいです。 しかもその後、13っていう忌み数を避けて内部バージョンを14にしたもんだから、VBもVB 14に。


dot.net にC#オンライン エディター

$
0
0

.NET Core、リリースされましたね。

まあ、その辺りの話は他の人に任せるとして。

自分が気になったのはこちら。

Scott Hanselmanのブログにこんな画像が

Scott Hanselmanの.NET Coreリリースに関するブログ記事に気になる画像がありまして。画像にリンクが貼ってあって、リンク先は

こちら。

ちょっと前に、「よくこのドメイン取れたな」、「マイクロソフトってURLにこだわってくれなくていつもダサいのに、これはほんとにうれしい」と話題になってたやつですね。 結局はhttps://www.microsoft.com/netに転送されたりはするんですが、まあ、http://dot.netがある、このURLでリンク貼れるってのが大事です。

で、このページをちょっと下にスクロールすると、こんなものが。

Starting coding

ウェブページ内でC#コード書いて試せる! いつからありましたっけ?

まあ、中身的には「Monaco」っぽいです。 Monacoを使って、チュートリアル コードをサイト内に埋め込んだり、「Run」ボタンを押して実行結果を出力したり。

これも、まあ、ずっとほしいほしい言い続けてたやつなわけですが。 Goなんかは公式サイト開いた瞬間「Try Go」なわけで。 同じものがC#にもほしいって言ってたら、ちゃんとできてた。 しいて言うなら、こんなちょっとスクロールしないと見えない位置に置くのはやめてほしいなぁという感じはあります。 このページはたびたび見たことあるけど、大体「DOWNLOADS」か「DOCUMENTATION」リンクに直行でスクロールしませんし。

しかも、チュートリアル ページのサンプル コードも、実行できるものは1個1個、このMonacoベースのオンライン エディターを立ち上げて実行できるみたいです。

チュートリアル ページのサンプル コード

これは大変よさげ。

「project.json辞めます」の意味

$
0
0

.NET CoreといいC#コンパイラーといいオープンソースになったおかげで常に最新の情報を拾えてありがたい限りなわけですが。 緩い段階のものが見えすぎて、いろいろ振り回されたりもします。

最近の話題というと、「project.json辞めます」騒動

これ、あんまり字面通り受け止めるとダメだと思うんですよね。 話の出どころは「ASP.NET Community Standup」の1コマ、5月10日放映分での会話らしいんですが。 こういう、会話中で出てきた言葉って、割かし不正確(にとられかねない緩い表現)なわけです。

ここでいう「project.json辞めます」の意味

.NET Coreは、当初、脱msbuildを目指していたわけです。.NET Coreにとっかかり始めた当時の発想としては、

  • msbuildとか、MS製のビルド ツールはC++、COM製で、クロスプラットフォーム化が大変
  • ASP.NET に対応できればいい
    • モダンなWebっぽいものを作りたい

というような感じだったはずです。

ところが今は、

  • msbuildが.NETでクロスプラットフォーム実装になってる
  • UWP、Xamarinなどなど、いろんなものに対応する必要が出てきた

と、状況が変わってる。

なので、実際のところは、「ASP.NET チーム主導で独自に作ってたビルド ツール辞めます」、「msbuild使います」という話になります。

で、「ASP.NET チーム主導で独自に作ってたビルド ツール」に名前がないもんだから、そいつが解釈するプロジェクト設定ファイルであるところの「project.json」を辞めるという言い回しをしてしまっているという状況。

もう1つのproject.json

.NET Coreのリリースがずるずると伸びてる間に、1年近く早く先にリリース段階に達した別プロダクトがあるわけです。UWPとかNuGet 3とか。

.NET Coreは標準ライブラリの参照でNuGetの仕組みに乗っかってるわけですが、 同じく、NuGetに乗っかりたかったのがUWP。 Windwos 10のリリースに合わせる必要があったので、2015年7月の段階でRTM。 そのために、NuGetも同時期にバージョン アップしました。

このとき、.NET Coreのproject.jsonのうち、パッケージ参照がらみの部分だけを抜き出して使うことになりました。 つまり、NuGet 3では、packages.configを辞めて、project.json形式を使うように。 ここで、project.jsonが2つに分岐したわけです。 この辺りは去年の12月にブログを書いてます。

そしてこちらのproject.jsonは、msbuild に組み込まれています。

先ほど言った通り、.NET Coreチームの言うところの「project.json辞めます」は、実際には「msbuild使います」の意味です。 つまるところ、NuGet側のproject.json (msbuild対応済み)は辞めるどころか、むしろ積極的に使われるようになる立場。

nuget.json?

で、パッケージ参照設定なのに名前が「project」なの?とかいう問題に至ります。 元々はprojectだったけども。その元々の方がお亡くなりになってしまい…

ってことで、NuGetが使っている方のproject.jsonはnuget.jsonにリネームしようみたいな話もあるみたい?

まあなんか、この「project.json辞めます」騒動のせいでもう、そこいら中から「project.json辞めたんじゃないの?」「UWPのやつどうなるの?」みたいな疑問の声が出まくってるんで、もう、リネーム必須な状況になってしまってるんじゃないかなぁ…という気がします。

project.json便利だったのに…

.NET Coreチームの言うproject.jsonは、.csprojほどファイルの衝突が起きなくて、ソースコードのバージョン管理的には大変ありがたかったです。

「project.json辞めます」、すなわち、「msbuild使います」というのは、結局、「.csprojに戻ります」という意味でもあります。 ありがたかった機能も一旦なくなるわけですが…

.NET Coreチーム曰く、「msbuild/.csprojの方を改善します」とのこと。csprojでも、「フォルダー中の.csは全部コンパイル対象」みたいなことをできるようにしたいみたいです。 てことで、「将来的に、msbuild/.csprojがもっと便利になる」と捉えるべき。

まとめ

緩い段階で出てきた話は字面通りに受け止めちゃダメというか。 今時は情報の出どころがものすごい緩いことが多いというか。 みんなタイトルだけ読んでわかった気になるんだから、緩い出どころを元にブログのタイトル付けちゃダメというか。

.NET Coreチームの言う、元々の意味のproject.jsonはどうやらなくなります。 ただし、その利便性をmsbuildに引き継ぎたいそうです。

「project.json辞めます」の本当の意味は「.NET Coreでもmsbuildを使います」です。 project.jsonでやろうとしていたことを、msbuildに取り込んでいきたいそうなので、「msbuildが便利になる」と考えるべきです。

また、NuGetの方のproject.jsonはすでに1年前からmsbuldに組み込み済みなので、なくならないどころか、むしろ今後積極的に使われるようになる側にいます。 ただし、nuget.jsonにリネームはするかも。

dotnetConf 2016 Japan

$
0
0

昨日はdotnetConf 2016 Japanでした。

あいにくの雨でキャンセル率高く、当初予想ほどの混雑には至りませんでしたが多くの方のお越しいただきました。 改めて御礼申し上げます。

以下、発表資料をまとめておきます。

dotnetConf 2016 Japan 開催にあたって ~ .NET の今と未来

デモ用ソースコード: https://github.com/chack411/dotnetConfjp

.NET Standard

Xamarinの新しい話とMonoの深い話

関連:

MVVM Done Right with Xamarin.Forms and Prism

.NET Core on RHEL

関連: .NET Core on RHEL リンク集 (RTM 1.0 時点)

ASP.NET Core

関連: dotnetConf 2016 Japan で ASP.NET Core 1.0 について話してきました

.NET Core/VS/VSCode他ツール類

ローカル関数

$
0
0

ちょっと間が空いたんで「C# 7思い出話」タグでなんか書いてたことを忘れかかっていたりはしますが。

C# 7のページにローカル関数の話を足しました

これで、現状で仕様が結構安定してるやつは全部なんですよねぇ。type switch辺りはそろそろ書いてもよさそうなくらい作業進んでは来てますが。タプル型とかはもうちょっと先かなぁ(自分の執筆ペースを考えるとたぶんちょうどいいくらいに安定しそうな気も)。

ローカル関数

これは本文にも書いちゃってるんですけど、要望として多いのは、ローカル関数がほしいというよりも、匿名関数の改良だったんですよね。

などなど。

そこに来て、ローカル関数ほしい?って話を出したところ、この手の匿名関数に対する問題が、ローカル関数なら結構解決するんじゃないか的な話にどんどんなっていって。数か月後には(正式に機能追加の提案に至る](https://github.com/dotnet/roslyn/issues/2930)。

まあ、元々、C# Scripting/Interactive由来っぽい感じはあったんですが。 スクリプト環境だと、常にメソッド内部にいるかのような文法になる(トップレベルにステートメントを書ける)んで、 そこにメソッドを書くのは、実際のところローカル関数を作るのと大差ない感じになります。 たぶん、今まで全然入れてくれなかった機能が急に出てきたのはこれのせいじゃないかなぁとか思います。

ピックアップ Roslyn 7/21

$
0
0

PDBファイルへのソースコード埋め込み

Proposal: Embed sources in PDBs #12625

提案されたの自体はだいぶ前だし、実作業を開始したのも2週間くらい前からなんですが、PRの方で出たフィードバックをまとめて1 issueにしたみたい。結構おもしろい。

Visual Studio上で、参照しているライブラリのソースコードにF12キーとかで飛びたいわけですが。PDBみたいなデバッグ用情報にソースコードがまとまってないと、ライブラリ(+ PDB)を公開する側としても、参照する側としてもかなり面倒。issueページ中に背景シナリオがいくつか書かれてますが、例えば、

  • ビルド時にソースコード生成していて、その生成される分についてはバージョン管理に含めていない。ビルド時生成分のソースコードも含めてパッキングしてくれる仕組みがないとやってられない
  • 会社的にソースコードを出すこと自体はOKだけど、ソースコードを置いてあるリポジトリはプライベート。ライブラリ利用者にアクセス権は与えられない
  • GitHubとかでソースコードを公開しているけども、そのGitHub上のコードをVisual Studioから参照できるようにする設定までは面倒すぎてやってられない

などなど。

あと、さらに、PDBをライブラリ自体に埋め込みたいみたいな話も。

Task-like型を返す非同期メソッド

5/4のブログでちょっと書きましたが、非同期メソッドの戻り値に、Task(System.Threading.Tasks名前空間)以外の型を返せるようにしたいという話が出ています。

で、なんかマージされてる。

Allow async methods to return Task-like types #12518

次のVisual Studio ”15” Preview (次は preview 4になるはず)には入る予定っぽい

この機能なんですけど、割かし最近まで形になってなかったんですよね。なので当然のように、「C# 7/VB 15に入るのかどうか」議論のターゲットにもなってなかった。 4月くらいからLucian (今、VBのPMやってる。元々の専門が非同期処理な方らしく、C# 5.0のasync/awaitにもがっつり関わってたみたい)が作業を開始。この時点でも、「まだLanguage Design Meetingの議題に上げる段階に至ってない」とか言ってたんですが。

その後どうもプロトタイピングがうまくいったようで、気が付いたらLanguage Feature Statusのリストに並び、気が付いたらマージされてました。

結構有用な応用例があるんで、C# 7に間に合いそうなのは結構うれしいかも。次のBuild Insiderオピニオン、この辺りの話にしようかなぁ。

ピックアップRoslyn 8/5

$
0
0

Feature Status: Finishing

C# 7のステータスがいろいろ更新されたみたいですね。

C# 7に入るもの、ほぼ確定したのかなぁ。 C# 7/VB 15には状況が「Finishing(最終作業中)」のものばっかり残して、他は+1(その次)行き。

この感じは、次に出るであろうVS "15" Preview 4が最後のプレビュー版ですかね。

最終作業

最終作業中ってのは、まあ、基本機能は実装済みで、後は実装してみて初めて分かった問題とかバグの修正、みたいな感覚ですかね。 僕が把握してる範囲だと、以下のような作業はしているはず。

まあほんとにこういう細かい作業をする段階という感じです。

見送られた方

じんぐるさんも書いてますけど、元々C# 7/VB 15の欄にいたのに、+1送りになったのもありまして。

まあ、動きがなかったのでうすうす感づいていたものの。以下の3つ。

  • Async Main
  • Source Generation
  • Throw Expressions

Async Main

プログラムのエントリー ポイントとしてTask MainAsync(string[] args)Task<int> MainAsync(string[] args)を認めてほしいという話。

これ、最大の問題は同期コンテキストをどうするかなんですよねぇ。4年近く前にブログに書いたことあるんですけど、GUI (WPFでもUWPでもWinFormsでも)では問題ないのにコンソール アプリで実行すると競合起こす場合がありまして。

これに関する解決案みたいな話が全く見かけなかったんで、今はまだ取り組んでないんだろうなぁと。

ちなみにもう1つの問題として、今、↓みたいな書き方してるコードが結構たくさんあるはずで、これの互換性を崩しかねないからって話もあります。

class Program
{
    static void Main()
    {
        MainAsync().Wait();
    }

    static async Task MainAsync()
    {
        ...
    }
}

こちらはまあ、void Mainの方を優先する、みたいなルールで回避はできるはず。

Source Generation

これは、いろんなところが関係するんで、それが先送りの理由かなぁと。要するに、

  1. C# にコード生成を前提とした新文法を追加する: replace/original キーワードの追加
  2. CodeAnalysis API にSourceGeneratorみたいなクラスを追加して、コンパイラー プラグインとして自作のコード生成処理を挟めるようにする
  3. Visual StudioやVS Codeに、ソースコード書き換え時や、ビルド時にSourceGeneratorを掛ける仕組みを追加する

という3つが必要。

1はあったんですよね。一時期はmasterブランチにもマージされてました。問題もいくつか出てはいたですが:

それよりは、2、3の作業が全然見えてこないなーという感じで。やっぱりC# 7には入れないんだ…

VS "15" Previewのリリース ノートでも、「VS "15"で新たに入る機能はIOperationです」としか書かれてなかったし。 (※IOperationは、C#とVBの両方に対して、単一のプラグインでコード解析・コード修正を掛けれる機能。)

特に今は、Visual Studio、Xamarin Studio、Visual Studio Codeの全部にこの手の仕組みを対応させたがってる節があって、この3者の間で調整してそうな予感が。C#チームの範疇を超えてますし、多少時間掛かりそう。

Throw Expressions

void Method() => throw new NotSupportedException();とか、x ?? throw new ArgumentNullException(nameof(x))とか、int.TryParse(s, out var x) ? x : throw new InvalidOperationException();とか書けるようにしたいって話。

これをやるんだったら、never 型(去年のブログとかBuild Insiderの記事参照)も入れたいとかあるんじゃないかなぁと。

あと、こいつが本格的に必要になるのはmatch式(パターン マッチングのドキュメントのMatch Expressionのところを参照)が入ってからなので、これがC# 7から外れた以上、throw式だけを先に入れる動機付けは弱いはず。

Design Notes 7/15

C# Design Notes for Jul 15, 2016 #12939

で、ちょうど、7/15のDesign Notesが公開されて、ここにOut Varで導入される変数のスコープについて書かれています。

これまで、C#の変数宣言は書ける場所を限定していたのもあって、「宣言しているブロック内がスコープ」というシンプルなルールになっていました。

ところが、C# 7では、

  • Out Var: int.TryParse(s, out var x) ? x : default(int?); みたいに、out引数のところで変数宣言できるようになる
  • Type Switch: obj is int x ? x.ToString() : "unsupported"; みたいに、is演算子で変数宣言できるようになる

という2つの機能が入って、こいつらは、式を掛ける文脈ならどこでも変数宣言できてしまいます。 ここで導入される変数 x のスコープはどの範囲になるべきかという課題がありました。

現状、とりあえず相当厳しい方に倒して実装しています。すなわち、「xは、その式を含むステートメント内でだけ使える」というものです。要するに、以下のコードはコンパイル エラーに。

static void X(string s)
{
    var value = int.TryParse(s, out var x) ? x : 0; // x はこのステートメント内でしか使えない
    Console.WriteLine(x); // もうスコープを外れてる。使えない。コンパイル エラーに
}

いろいろ検討した結果、これはさすがに実際にありそうな用途をカバーしきれないという結論で、制限緩和を考えてるみたいです。

パッと見、「この式を含んでいるブロック内で使える」というのが素直に思えるんですが、if とか for とかが絡むと多少めんどくさく。

例1: if の条件式内で宣言した変数は、else側には伸びてほしくない

if (o is bool b) ...b...; // b はスコープ内
else if (o is byte b) ...b...; // bool の方の b はもうスコープ外。新しいbを作れる
...; // どちらのbもスコープ外

例2: 「含んでいるブロック内」で区切ると、forとかで変になる

for (int i = foo(out int j); ;) ;
// iはスコープ外だけど、jはスコープ内になってしまうのはいい?

例3: ブロックがないifとかどうするの

if (...)
    if (...)
        if (o is int i) ...i...
...; // ブロックを区切りにすると、i はここもスコープになる
     // その手前のifのせいで、iが初期化されている保証できないけどいい?

ってことで、ifとかforとかの場合は「embedded statements内にスコープを限定する」っていうルールにしたいそうです。 embedded statementsって、要するに、ifとかforとかの直後に書けるステートメントのこと。ifとかforとかの後ろには、ブロック、もしくは、何か1つステートメントを書けるわけですが、その部分のことを指すそうです。

余談: embedded statements

この話を見て、embedded statementsって何だっけ?って思って久々にC#の仕様書を眺めてみたんですが…

statement:
    labeled-statement
    declaration-statement
    embedded-statement

embedded-statement:
    block
    empty-statement
    expression-statement
    selection-statement
    iteration-statement
    jump-statement
    try-statement
    checked-statement
    unchecked-statement
    lock-statement
    using-statement 
    yield-statement

つまり、embedded statement = ステートメントのうち、変数宣言とラベル付きステートメント以外。

ということは、こうなる: https://twitter.com/ufcpp/status/761514377543462912

変数宣言はブロックなしでは書けない

まあ、要するに、変数宣言とラベルって、それを囲っているブロックが重要な構文なので、ブロックを省略するとか認めないっていう。言われてみればそうですよねという感じ。

(逆に言うと、そうまでして変数宣言できる位置をちゃんと絞ってたのに、Out VarとType Switchのせいで面倒になったという話。)

ピックアップRoslyn 8/6

$
0
0

Source Generatorが間に合わなかった理由

昨日のブログにある通り、C# 7に入る機能リストが更新されて、入るもの・入らないものに変化があったわけですが。

それと関連して:

https://github.com/dotnet/roslyn/issues/12630#issuecomment-237959967

やっぱり、Source Generatorが外れたのがショックだっていう人が現れて、それに対してC#チームの人が釈明してたりします。

まあ、大体昨日書いたことの通りですね。 IDE側の対応が大変で今回は無理とのこと。

Source Generatorって物自体が、IDE側にとっては結構大きな課題でして。例えば、コード生成によってプロパティなどのメンバーが増えたりするわけですが、その生成されるメンバーをIntelliSenseに出したりしようとすると、文字入力のたびにSource Generatorが走り、構文解析しなおすのかという話になります。IntelliSenseなんかは特にパフォーマンスにシビア(ここがもっさりしているとIDEのユーザーのストレスが非常に大きい)なので、Source Generatorが悩ましい存在になります。

もちろん、間に合ってるC#言語機能(replace/original)だけでも先に入れれないかなんてことも言われています。 けども、今早まって入れても、IDE側も対応できるようになった頃にはもっといいアイディアが出るかもしれない。より良いものがもし入ってしまったら、早まって入れたreplace/original構文が無駄・邪魔になってしまう。そういう状態にはしたくない。という話も。

ってことで、Source Generatorはもう少し成熟を待たないといけないということになります。

Sprint Summary

最近結構、「C#チームの中の様子が見えない。クローズだ。Roslynプロジェクトはオープンソースなんじゃなくて、オープンダンプ(クローズに作ったものをオープンにコピーして出してるだけ)だ」的なことを言い続けてるtroll(荒らし認定される勢いでしつこい人)がいらっしゃったり。

まあ、Roslynプロジェクトは確かに3000件ものIssueがオープンなまま整理されてなったりしますし、C#チーム内で起きたことも定期的には出てこない(ふと思い出したかのように出てくる)んで、あんまり擁護もできないんですが。

ということで、C#チームの人が重い腰をあげて「スプリントごとの作業概要」を公開。

Sprint 104 Summary #12974

まあ確かに、これくらいはちゃんと定期的に出そうよ…


ピックアップRoslyn 8/10

$
0
0

7月のデザインノートが2件ほど。

これ関連の作業がひと段落したところでまとめて清書して表に出したって感じですかねぇ。 この辺りの話、かなりの割合がもう実装されててマージされてたりします。

先週、dots.をお借りしてこんなイベントやってたわけですが

最新のmasterブランチの取ってきてビルドして実行してみると、大体この仕様通りになってる感じ。

さて、どんな感じの仕様かというと…

タプル型のメンバー名は省略・名前付きの混在可能

こんなコードでOKですって。

var t = (1, y: 2); // infers (int, int y)
(int x, int) t = (1, 2);

ちなみに、名前を省略したところは、ValueTupleがたの本来のメンバーである x.Item1 とかの名前で参照できます。

ITuple

タプル型みたいな「単に複数のデータを寄せ集めただけ」な型に対して、インデックスでメンバー参照したくなることがあります。 ValueTuple型はそのために、以下のようなインターフェイスを実装すべきじゃないかという話に。

interface ITuple
{
    int Size;
    object this[int i] { get; }
}

タプル型の分解に使いたいそうで。

ValueTuple型はこれを実装すべきだとは思うものの、名前にはまだ議論の余地あり。 インターフェイス名もIDeconstructableとかがいいかもしれないし。 要素数のプロパティもLengthとかCountとかもあり得るし。

var型がある場合。

C#のvarは、文脈キーワード(特定の文脈でだけキーワード扱いされる)です。varって名前のクラスがあると、クラス名として認識される。

で、タプル型の分解構文で以下のような書き方を認めることになるわけですが、

var (x, y) = e;

ここで、varクラスがあった場合どうなるべきか。

class var {}
var (x, y) = e;

ちなみに、世の中には、わざわざこういうvarクラスを用意しておくことで、型推論のvarを使わせない(コンパイル エラーにさせる)トリッキーな運用をしている人もいるそうで。C#チーム的には「(その良し悪しは置いといて)そういう運用も認めるべきでしょう」という感じ。 そういう背景もあって、タプル型の分解におけるvarでも、varクラスがあったらコンパイル エラーにするみたい。

var メソッド

じゃあ、メソッドの場合はどうか。分解代入の構文、メソッド呼び出しに似ているので、以下のような書き方ができてしまいます。

ref int var(int x, int y);
var(x, y) = e; // deconstruction or call?

参照戻り値なメソッドへの代入(参照先への代入)か、分解代入か、どちらにするべきか。

常に分解代入の方を選ぶそうです。メソッドの方を呼びたい場合は @var って書けばできます。

partialクラスでのインターフェイス

partialクラスの場合、複数の宣言で、同じインターフェイスを継承できたりします。 ここで、じゃあ、メンバー名違いの同じ型のインターフェイスを継承してしまった場合はどうするべきか。

partial class C : IEnumerable<(string name, int age)> { ... }
partial class C : IEnumerable<(string fullname, int)> { ... } 

タプル型は、内部的には全部ValueTuple構造体に変換されます。 名前は属性に残るだけ。 で、じゃあ、上記の名前違いのインターフェイスは別の型なのか同じ型なのかよくわからず。 紛らわしいのでコンパイル エラーにすべきでしょう。 逆に、メンバー名も含めて全一致している場合だけは、複数のpartial宣言に書いても大丈夫。

もう少し面倒なケースは、多重継承(インターフェイスであればC#でも多重継承が可能)。 以下の場合はどうすべきか。

interface I1 : IEnumerable<(int a, int b)> {}
interface I2 : IEnumerable<(int c, int d)> {}
interface I3 : I1, I2 {} // what comes out when you enumerate?
class C : I1 { public IEnumerator<(int e, int f)> GetEnumerator() {} } // what comes out when you enumerate?

現状、これもコンパイル エラーにする案で進めてるみたい。 できてそこまで大きなメリットもなさそうなので、複雑化させない方向に倒すという感じ。 もし、将来的にこれを認めたくなるような重要な利用シナリオが見つかったりした場合、それはその時に考える。

タプル リテラルの分解

null (全ての参照型に代入可能)とか、1 (intshort, byte 辺りのどれか不明瞭)とか、リテラルの場合、型があいまいなものがあります。 その分解はちゃんと働くべきか。

(string x, byte y, var z) = (null, 1, 2);

できるべきだろうとのこと。

各要素ごとに並べて書いた時と同じ挙動になるべき。上記コードであれば、まあ、↓みたいなのと同じ解釈をすべき。

string x = null;
byte y = 1;
var z) = 2;

ただし、これが逐次実行されるわけじゃなくて、一斉に代入が起きる。つまり、swapに使っても差し支えないようなにはなってる。

(x, y) = (y, x); // swap!

タプル型の中のvar

「タプル型の変数宣言」と「分解代入」は非常に似た構文になるわけですが。

(int x, int y) = GetTuple(); // 分解
(int x, int y) t = GetTuple(): // タプル型の変数宣言

じゃあ、以下の構文(これも似て非なるもの)の場合はどうなるべきか。

(var x, var y) = GetTuple(); // これは分解代入時の型推論
(var x, var y) t = GetTuple(): // varなタプル型。これは認めるべき?

で、結論的には、この後者は認めないとのこと。

分解代入の戻り値の型は void?

C#では、代入は式です。どこにでも書けます…

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

まあ、ろくでもないんですが。副作用を伴う式とか割かし害悪。C言語を参考にしすぎたところですね。とはいえ、今更変更できません。

例えばの話、forステートメントの中には式を書くことになっているので、以下のようなコードを書きたければ、タプルの分解代入も式でないといけないそうです。

for (... ;; (current, next) = (next, next.Next)) { ... }

とはいえ、実のところ、「戻り値がvoidの式」という扱いにすれば、forステートメントの中で使えつつ、さっきのろくでもないy = (x = 2) * x みたいなコードをなくせたりします。

ということで、voidであるべき?

まあ、これも、既存の代入式との一貫性がなくなるので、voidではなく、タプル型を返すべきだと思ってるみたいです。 C# 7では実装しなさそうだけど、後々は、分解代入の結果を、再度タプル構築して戻り値に返すべきだと思っているとのこと。

参考までに: Swift

ちなみに、Swiftはほんとに、代入は戻り値がvoidの式みたいです。 y = (x = 2) * x なんていうクソコードは認めません。

その割にインクリメント・デクリメントがあった y = ++x * x とか書けたわけですが。 そりゃ、forステートメントもインクリメントもなくしたくもなります(Swift 3で破壊的変更してまでなくす予定)。

分解を変換として、変換を分解として

分解代入と型変換はある程度似た構文です。分解は、タプル型への変換的な雰囲気があります。似てるのあれば、いっそある程度統一性を持たせるべき?

まあ、そうしない方がよさそう。分解(コンパイル結果的にはDeconstructメソッドの呼び出し)は型変換的に扱われるべきじゃない。

匿名型

匿名型({ X = 1, Y = "a" } みたいなやつ)はDeconstructメソッドやITupleインターフェイス実装を持つべき?

そうでもなさそう。実装しても、今のところ有用な利用シナリオが思い当たらないとのこと。 欲しくなる場面もなくはないけど、そういう場面では大体タプル型を使えば解決しそう。

分解代入時のワイルドカード

ワイルドカードってのは、要するに、要らない部分を読み飛ばす機能。

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

こういうコードで、3を読み飛ばすために使いもしないダミー変数を用意する必要はありません。

C#的に、こういう機能を入れるべきだろうとは思ってるみたい。 ただし、たぶん、C# 8になる(7には入らない。パターン マッチングと同時期に入る予定)。

あと、ワイルドカードのために使う記号はたぶん *。 関数型言語の類だと _ を使うことが多いんですが、C#では _ が有効な識別子になっちゃうので。 既存コードの意味を変えてまではこの記号は使わないみたい(コード解析をきっちりやれば不可能ではないけど、そうまでするかという話)。

double型に対するswitch

パターン マッチングが入った暁には、double型の変数もswitchに使えるわけですが。 ここで問題になるのは、double型の等値判定。

NaN(Not a Number)の扱いどうするの?とか、実は==EqualsでNaNとの比較結果が違ったりするけどどうする?とか。

==Equalsの違いというと、intの1とdoubleの1.0が等値判定とかも。前者はtrueになるけど、後者はfalse。

Equalsの側を使いそう。

ピックアップRoslyn 8/16 Close祭り

$
0
0

実作業が一段落つくと始まるissue整理。

すごい数一気にコメントついたりCloseされたり。以下、そのまとめ。

やらない

1行目: どういうものか

2行目: やらない理由

Proposal: Fixed-size buffers enhancements #126

構造体の内部に固定長配列を持てるようにしたい。

CLRのレベルで対応してもらいたい。C#コンパイラーでのコード生成はメリットの割に複雑すぎる。

infoof / propertyof / methodof (and reflection objects in attributes?) #128

infoof演算子(info-of、typeofのノリでMemberInfoを取得する)が欲しい。

リフレクションを言語に組み込みたくはない。nameofでもかなりのシナリオをカバーできる。

[Proposal] try-catch expressions #132

try-catch を式にしたい。

このページで提案されている構文だと問題がある。再提案があれば、取り組む価値があるかはまだ検討の必要があるかもしれないが、現状そこまでには思っていない。

Language support for the decorator design pattern #124

デコレーター(PythonのデコレーターとかKotlinのclass delgationと同種の機能)が欲しい。

いくらかのメリットがあるのはわかるし、過去に似た機能の提案も見てきた。でも、コンパイラーが暗黙的にコード生成するような機能には心配事が多々ある。 今なら、ソース ジェネレーター (たぶんC# 8)で解決できるかもしれない。

Strongly-typed type aliases #58

同じ型なんだけど、互いに暗黙的型変換はできないエイリアスを作りたい。例えば、EmailAddress型(中身はstring)とstringを区別したい。

ソース ジェネレーター (たぶんC# 8)を使って構造体をコード生成して、同様のことを実現してほしい。ソース ジェネレーターで苦痛は減るはず。

Auto properties with setter blocks #123

プロパティで、getのみ自動実装で、setは手動実装できるようにしてほしい。

良いディスカッションはあった。ディスカッションの中で出てきた副次的な案は別のissueページに移った。 でも、このページで提案されている元々の提案はやらない。バッキング フィールドを参照するような機能は得られるものに対して労力が過剰。

Implement Units of Measure or a broader class of similar contracts #144

F# のUnits of MeasureがC#にもほしい。

非常にクールな機能だと思うものの、同種のものをC#に追加するのはメリットの割にコストが大きい。

訳注: たぶん、上記のStrolngly-typed typle aliasesで十分な利用シナリオも多いと思う。

[Proposal] Better handling of multiple comparisons with the same variable (a > min && a < max) #136

min < x < max みたいな、数学でよく見る多項比較できるようにしてほしい。

C# ではすでに a < b < c(a < b) < c の意味で有効な式になっているし、難しい。 e in (0 ... n) みたいな文法も、e < n なのか e <= n なのか混乱の元。

Allow type parameters to be declared co/contra variant on classes. #171

クラスの型引数にも共変性・反変性を認めてほしい。

CLRレベルでの対応が必須。

Proposal: Virtual arguments in methods #176

引数の実行時の型でメソッドのオーバーロードを呼び分けれるようにしてほしい(いわゆる多重ディスパッチ)。

ちゃんとやるのであればCLRレベルでの対応が必須。 型スイッチ(C# 7で入る)があれば書くのが多少楽になるけども、多重ディスパッチは型スイッチと比べてかなり複雑。

Feature Request: Lighter syntax for doc comments #85

docコメントがXMLなの重たい。JavaDoc/JSDocみたいな文法ほしい。

現状に不服があるのはわかる。でも、新旧文法の混在・競合みたいなのは嫌だし、直近での検討はしない。

重複削除・もうやってる

1行目: どういうものか

2行目: 何と重複してるか

Allow local variables to be static and/or readonly (deeper scoping) #49

ローカル変数にstatic(スコープが関数内に限られた静的フィールドを作る)とかreadonly(書き換え不能)とか付けれるようにしてほしい。

以下の2つに分割できる。

Allow C# to use anonymous iterators. #24

匿名関数でイテレーターを作らせてほしい。

C# 7で入るローカル関数で同じことができる。

Please provide the C# Specification in PDF instead of DOCX #31

C#の仕様書をPDF提供してほしい。

長期作業として、マイクロソフトC#仕様書とECMA標準規格書の統合作業をしている。ECMAの方のはPDFで提供されてる。

訳注: 現体制になってから全然仕様書が表に出てこないんだけども… Long termってどのくらいLongなんだろう…

Lambdas omitting parameters #20

ラムダ式の引数のうち、要らないものは無視できる構文が欲しい。someFunction((_, foo) => fooだけ使う); みたいなの。

パターン マッチング(たぶんC# 8)と一緒にやる。

Extension properties for classes #112

メソッドだけじゃなくて、プロパティも拡張で作れるようにしてほしい。

以下の提案の一部分になる。

UnreachableAfterAttribute and unreachable code detecting #59

常に例外を出すことが分かっていて、呼び出しよりも後ろのコードには絶対に到達しないことが保証できるようなメソッドを作りたい。そのためにUnreachableAfterみたいな属性を作ってほしい。

neverってのを検討中。

引き続き検討

1行目: どういうものか

2行目: どう取り組んでいるか

[Proposal] Object initializers for factory methods #133

new T { X = ... } みたいなのを、new演算子ではなく、ファクトリー メソッドを使った場合でも使えるようにしてほしい。T.Create() { X = ... } みたいな。

with (たぶんC# 8になる)と合わせて考えたい。

Do not require type specification for constructors when the type is known #35

var (右辺値から変数の型を推論)の逆で、T x = new();みたいに、左辺値からnewの型推論をしたい。

分解(deconstruction)の対となる構文として魅力的になりそう。

訳注: 分解の方がvar (x, y) = tuple;みたいに型推論が効くんだから、構築(new)の方にも型推論が効いても良さげ。

IAsyncDisposable, using statements, and async/await #114

非同期Disposeがしたい。usingステートメントで、IAsyncDisposable的な非同期版のインターフェイスを使えるようにしてほしい。

Lucianにタスク割り当て。

訳注: たぶん、非同期シーケンスと一緒に検討。Lucian はTask-likeとか含め、非同期処理がらみの新仕様に取り組んでる人。

Extend LINQ syntax #100

クエリ式の後にToList()とかSingle()とかの、シーケンス自体に対する操作を呼ぶのが、今の構文だとだるい。

バックログに入れておく。from x in items where x.foo, select x.bar do Single() みたいな構文がいいかも。

[Proposal] (Minor) Method groups for constructors #140

コンストラクターをメソッド グループ(デリゲートにメソッドを渡すために、Action a = Methodとか書くやつ)として認めてほしい。

よくある要求だし、よいアイディア。

Visual Studio "15" Preview 4

$
0
0

Preview 4が出たみたいですね。

最近、まあ、Preview の新しいのが出ても、C# vNext の進捗具合だけしか取り上げてなかったりするんですが。 今回も主にその話題で。

インストーラー

1点だけ。新しいインストーラーがだいぶちゃんとしたものになってますね。 細かい機能を選択して入れるんじゃなくて、「UWP開発したい人はこのオプションを選択してください」みたいな感じのUIに(その結果、それに必要な機能に一通りチェックが入る)。

Visual Studioってものすごいインストールに時間が掛かるので有名ですが、結構な割合、エミュレーターとか仮想マシンのインストールに取られてる時間だったりします。 要するに、UWPとかモバイル開発が不要なら、ここ外すだけでかなりインストール早かったり。

C# 7 進捗

C# 7で入るものの予定は以下のページ参照。

これを踏まえて、Preview 4でのC# 7実装の状況まとめ:

ValueTaskは、ちょっと前に中の人が「Preview 4に入る予定のブランチにマージされたぜ」ってtwitterでつぶやいてましたが、 QA通らなかったのか、リバート食らってました。

他は、たぶんあと細かい調整だけですかね。 変数/ローカル関数のスコープとか、クエリ式中でも分解構文使えるようにするとか。

ちなみに、タプルを使うためにはSystem.ValueTupleが、ValueTaskを使うためにはSystem.Threading.Tasks.Extensionsが必要になりますが、 どっちももうNuGet.orgに並んでいます。

ピックアップRoslyn 8/27: What’s New in C# 7.0

$
0
0

ブログ: What’s New in C# 7.0

Roslynリポジトリ内の話じゃないんですけど、Preview 4でのC# 7の動きについて、Madsがブログ書いてました。

7.0

そういや、この文章だと「C# 7.0」。 昔ちょっとブログ書きましたけど、最近って小数点以下のバージョン付けないんですけど。 C# 7はずっと「7」ばっかりみてたけど、久々に「7.0」表記。 この辺り、最近、C#チーム内でも統一してないなぁ…

C# 7.0の内容

このブログの内容的には、 先日僕もブログで書いたのとそんなに差がないんで詳細は省略。

差分というか、僕が書いた方で漏れてるのは、以下の内容:

out varのwildcard

out varと一緒に、*、要するに、受け取る必要のない out 引数を無視するための「wildcard」も入れるかもという話もあるみたい。

*は、 元々、パターン マッチング構文の1機能(パターンの1種)です。 で、out varもパターン マッチングと同列で検討されてた機能です。

今、C# 7で、out varだけは先に入れる方向で実装が進んでいるわけですが、どこまで一緒に前倒すかという問題。 out varに対する*はC# 7で入れるかどうかまだ迷ってるっぽいです(C# 7時点で入らないとしても、たぶんパターン マッチングとは同時に入る)。

More expression bodied members

今、メソッドとget-onlyなプロパティに限り、式が1つだけの実装に => を使えるわけですが。 それを、コンストラクター、デストラクター、getとset両方あるプロパティに対しても使えるようにするやつ、C# 7で入りそうらしい。

この機能、完全にコミュニティ主体の実装なんですよね。 3月にブログ書いてますけど、この機能はずっと「需要がある程度あるのはわかるけど、メリット小さめだし後回しになってる」みたいな状態でおいておかれてたものなんですけど、それに対して、「実装したよ」ってプルリクエスト送ってきた人がいて。 C#チームが「そのプルリクを取り込むことにした」って書き込みはしてたんですが、その後音沙汰なしでした。 久々に見たと思ったら、C# 7のタイミングで取り込むことにしたんですねぇ。

Throw expressions

これ、結局どうなるんだろ…

Language Feature Status」からは、むしろ、つい最近「7じゃなくて7の次で」降格したところなんですけど。 どっちが正しいんだか…

MSDNブログ

そういや、気が付いたら、MSDNブログから「C#チーム ブログ」「VBチーム ブログ」は消えてるんですよね。 Roslynのgithubリポジトリに一本化して、もうブログは書かないのかと思ってたら。

.NET配下でブログ書きますか。

コメント欄

で、なんだかんだ言って、ブログはコメントたくさんつきますね…

いくつか、気になったの拾っておきます。

contract

「design by contract」については何もないの?とのコメントが。

返信ついてて、「今のタイミングでは何もしない。contractはいまいち煩雑で複雑になりがちだし、そんなに大々的な検討もしてない。contractの用途的に、9割がたはnullチェックに関するものだけど、それは型システム指向で『非null参照型』を次のメジャー リリース向けに検討してる。」

VB

VBは?沈みゆく船だからC#に移った方がいい?

に対して、他の人から「頼む、ほんとお願いだからVBの質問しないで。死なせてあげて」とかついてる。

さらにそれに対して、VBのPMの人が「頼む、ほんとお願いだからVBの質問し続けて。フィードバックもらえることがほんとものすごい重要だから」とか返信してるという。

どこの国でも似たような状態ですよねぇ。 VBが死ぬときは、今のVBユーザーが絶滅したときであって、VB使ってない外野がとやかく言うなよっていつも思うんですが。

wildcard? *なの?nullとかでいいんじゃない?

*とかきもくない?↓みたいな、null使う文法の方がよくない?とか言ってる人が。

p.GetCoordinates(out int x, null); // Ignore return
p.GetCoordinates(out int x, out null); // Alternative ignore return

何この、見た印象ぬるぽ起こしそうな構文… 値を受け取るべき場所にnull書くとかちょっと…

そして飛び火して、本題からそれて「nullはこの世から消えるべき」主張をこのコメント内でし始める人まで現れる始末。

タプルとout var両方やるの?

タプルもout varも、多値戻り値を返すために使う(ことが多い)構文なわけですが、 なので当然「どっち使えばいいの?なんで両方やってるの?」という質問も。

それに対する答え:

  • だいたいの場合においてはタプルを使うといいよ
  • ただし、以下の場合においてout引数が残る余地がある
    • 複数のオーバーロードを用意したい場合(タプルというか、戻り値だとオーバーロードできない)
    • Tryパターンなんかだと、単一のboolを返す意味がある。ifステートメントの条件式とかで使えるように

ちなみに、別のコメントに対する返答で、今このタイミングでout引数に対する拡張を入れる意味について「パターン マッチングとout varには相乗効果がある」との回答あり(要するに、パターン マッチングのついで。パターン マッチングをやるならこちらも)。

C#がどんどん汚染されてない?

だいたいの人は「便利!待ち遠しい!」って言ってる中、まあ、毎度必ずいるわけですが「ちょっとトリッキーすぎない?泥沼にはまりそうで嫌だわ」って言い出す人が。 C# 7に限らずこれまでも、C#に限らずありとあらゆる言語でこういう意見絶対出てくるわけですが。

それに対する Mads の回答:

あなたは言語の進化のある種の核心をついてる。我々は機能を足せるけど、絶対に消せない。C#の進化においてもこのことが重くのしかかってる。 Anders (元祖、C#/.NETの父)の発言は有名だけど、「あらゆる機能はマイナス100ポイントから始めろ」と言ってる。 全ての機能について、我々は誰が喜ぶか、今現在だけでなく、今から10年後のことも考えるようにしている。 今現在有用かどうかだけでなく、永久に言語の一部分を占め続けるということを考えるようにしている。

我々はだいたいは正しいが、常に正しいわけではない。わかるだろうが、未来というのは予測が最も難しいものの1つだ。 このことは、言語設計のアプローチに対する異なる側面をもたらす。 我々は大胆でなければならない。 我々は遠い未来への責任に追い詰められてはいけない。 なぜなら、さもなくばその未来も得られないから。 C#は今この時「最高のプログラミング言語」の1つでなければならない。 さもなくば、明日にはそうではなくなってしまう。

なので、我々は少々アグレッシブに機能を追加する。 開発者にとって最も価値ある機能を追加しようと試みる。 さらに、その機能を追加するにあたって、言語の複雑性が増すという観点から不利益が最小になるよう試みる。 たとえばそう、パターン マッチングは新しい機能になるわけだが、しかし、既存の言語要素、例えばisswitch、既存の型の中で動くものだ。 それは(うまくいけば)我々の過去とよく統合されている。 同時に、いくつかのパターンを、将来の発展の余地を残しつつ、今現在の「税負担」にならないように導入している。 それは、我々のもっともらしくあり得そうな未来とよく統合されている。

信じられないかもしれないが、我々が何パターンかあり得そうな未来について、取り組む余地を確実に残せるよう、投機的な設計にどれだけ時間をついやしていることか。 これは、我々が、新機能を追加するにあたって、この言語を長期的にわたって健康で生存力がある状態に保てるよう試みていることの一部分だ。

以上で、我々がC#の長期的な進化についてどのくらい考えているか、その姿を伝える助けになれば幸いだ。

タプル

$
0
0

思い出したかのように「C# 7思い出話」。

タプルがらみも先週末で一通り書き終わったはずかな。

タプル

これ自体はおおむね当初予定通りですかね。 そんなに裏話的な面白話もなく。 文法的に変化が大きいんで実装は大変そうでしたが。

しいていうと、他の提案を差し置いてタプルの優先度が高かった的な話ですかね。 タプルは、レコード型とか、 C# 7のさらに先で入る予定の機能の基礎に使う可能性があって。 依存順的に先にないと行けないという、割かしシンプルな話。

タプルは、(int x, int y)みたいなメンバー名を持った型から、ValueTupleという完全に名前のないデータ構造を生成する機能なわけで、 型に緩い世界と厳しい世界の橋渡しとして期待されます。ロジックを書く上では厳しい型付けを持っている方が楽ですが、View (WPFやUWPのData Bindingみたいな世界)や、Wire Data (通信層でのデータのシリアライズ・デシリアライズ)とかでは緩い型の方が助かったりします。その間をつなぐ基礎に使えそうって意味で、タプルには期待感があります。

そして、レコード型をタプルをベースに作る、あるいは、レコード型からタプルへの変換を用意すれば、型名がある世界とない世界もつなげます。

レコード型をタプルをベースに作るっていうのは、例えば以下のようなコードを書いたとして(これがレコード型って呼ばれる機能)、

class Point(int X, int Y);

以下のようなコードに展開するという案。

class Point
{
    (int x, int y) Value;
    public int X => Value.x;
    public int Y => Value.y;
}

レコード型からタプルへの変換を用意というのだと、以下のような感じ。

class Point
{
    public (int x, int y) Value => (X, Y);

    public int X { get; }
    public int Y { get; }
}

どちらにしろ、依存関係があるなら、依存されている側の方が先に落ち着いていないといけない。

レコード型は、その前身となる primary constructorっていう機能提案から考えるとC# 6よりも前(一瞬、C# 6に入れる想定で話が進んでた)、2014年半ば頃からあります。それが、「もっと洗練してレコード型にしてから取り入れたい。そのためにはC# 6では無理」となり、「タプルやパターン マッチングと併せて考えたい。そのためにはC# 7よりも後」となり今に至ると。

分解

こちらは逆に、前倒し。 前倒しというか、パターン マッチングがらみの仕様のうち、一部分だけが先行してC# 7に入るわけですが、その一部。

パターン マッチングは結構大きな提案です。All or Nothing、「全部入れるか全部入れない」かとかで入れようとすると、たぶん3年くらい先になります。 こないだ短期リリース サイクル化の話とか書きましたが、後への影響残さないなら、先に少しだけリリースする方が助かります。

元々は、以下のような構文で話が進んでいたんで、それからするとずいぶん、分解専用の構文になりました。多少、そこは、今後、パターン マッチングの全体が入るときに衝突しないのかな?とか思ったりはします(たぶん大丈夫という判断が付いたからC# 7で入ることになったはずですが)。

let パターン = 分解したいもの;

Unity 5.5でasync/await使えた話

$
0
0

Unity 5.5 ベータを入れてみたという話。

ブログ曰く、「Mono C# コンパイラー がMono4.4にアップグレードしました。」とのことなので、これ、C# 6使えるはずよね?と。

公式サポートはC# 4

リリースノートを見ると、「C# 4です」って書かれてるわけですが。 Mono 4.4のコンパイラーを使っててC# 6使えないとかお前は何を言っているんだ…

まあ、標準ライブラリの方が .NET Framework 3.5相当のままなので、Taskクラスが使えない。 なので当然、普通にやるとasync/awaitが使えない。 その状況下で「C# 6対応」とか言っちゃったら、問い合わせ押し寄せてやばいでしょうから、公式には「C# 4」と言わざるを得ないのはわかります。

要するに、コンパイラー的にC# 6に対応している状況で、langversionオプションを指定してわざと4に絞ってる。

例えば、以下のようなコードを用意します。

async/await (C# 5)、expression-bodied method (C# 6)とか使ってます。 これをコンパイルしようとすると、 Unity 5.3だと以下のようなエラーに。

Assets/Scripts/NewBehaviourScript.cs(30,13): error CS1519: Unexpected symbol `=>' in class, struct, or interface member declaration
Assets/Scripts/NewBehaviourScript.cs(37,28): error CS1519: Unexpected symbol `XAsync' in class, struct, or interface member declaration

これが、5.4は試してないんですけど、5.5だと以下のように。

Assets/Scripts/NewBehaviourScript.cs(30,12): error CS1644: Feature `expression bodied members' cannot be used because it is not part of the C# 4.0 language specification
Assets/Scripts/NewBehaviourScript.cs(37,16): error CS1644: Feature `asynchronous functions' cannot be used because it is not part of the C# 4.0 language specification

5.3曰く「ちょっとその文法わからない」、5.5曰く「その機能はC# 4じゃないからダメ」。 5.5は解釈自体はできていると。

-langversion:6

てことで、まあ、オプション変更。

プロジェクトの Assets 直下に mcs.rsp っていうファイルを作り、

Unityに対してC#コンパイラー オプション指定

以下の1行を書いて保存。

-langversion:6

これで、C# 6なコードもビルドが通ります。

ただ、Unityが出力する csproj ファイルに余計な行が入るんで、こいつも消さないと、Visual Studio上でC# 6の機能が使えなくなります。

余計な1行

この行も削除が必要。 いったん手作業削除してるんですが、ちゃんとやるならProject File Generationでも使ってフックしてやればよさそう。

Taskクラスが必要なasync/awaitはともかく、ライブラリ依存がほとんどないC# 6の機能とかなら問題なく使えると思います。

async/awaitを使う

で、Taskクラスさえ自前で用意すればasync/awaitも使えるっぽい。 うちは自力でバックポートしたライブラリを持ってるので、それを参照してみたら、あっさり動きました。 ライブラリは↓これ。

実際動いてるプロジェクトがこんな感じ↓。

まあ、Unity Editor上でしか試してないんで、IL2CPPとか実機で動くかはわからないんですが。

プログラミング言語における文字コードの話

$
0
0

世の中がほぼUnicode前提になってめでたしめでたし。とはいかなかった現実の話。

String型でできる文字列処理とか、ソースコード自体、特に識別子で使える文字とか。

軽くおさらい: Unicode

まあいろんなところでいろんな人が書いてると思うのでさらっと概要だけ。

Unicodeは、元々、「65,536文字あれば十分だろ」とかいう幻想の元、2バイト固定長の文字コードとして作られていました。 もちろん足りなくて、ビット数を拡張。基本が2バイトのままでこの拡張した分を取り扱えるようにしたのが今のUTF-16で、拡張分は2文字分(4バイト)を使って表現。 この、2文字分使って1文字を表すやつのことをサロゲートペア(surrogate pair: 代理対)と呼びます。

あと、ASCII文字も2バイトになるのを欧米人が嫌って、ASCII文字はASCIIコードのまま、逆に漢字・ひらがな・カタカナなんかは3バイトになるようなエンコード方式ができて、それがUTF-8。 固定長文字列とか幻想でした。サロゲートペアとか出てくるくらいならUTF-8でいいでしょう…

UTF-8やUTF-16へのエンコード前の状態、Unicode的に1文字として割り当てられているコードを、コードポイント(code point: 符号点、符号位置)と言います。 Unicodeコードポイントは今のところ21ビットの数値。

コードポイントこそが文字の最小単位かというとそうでもなくて、アクセント記号とか濁点とか、他の文字と組み合わせて使うことで、表示上は1文字に見えるけどもコードポイントは複数になる文字があり得ます。 「か」(U+304B)+「 ゙」(U+3099)で「が」1文字に見えたり、 「a」(U+0061)+「 ́」(U+0301)で「á」1文字に見えたり。

その他、漢字の異字体セレクターとか、絵文字の肌トーンとか、複数コードポイントで1文字になるものが結構あります。 こういう、表示上は1文字として扱うべきという単位を書記素クラスター(grapheme cluster)と言います。 UI的に、文字の選択や削除は書記素クラスター単位で行うのが好ましいです。 さもなくば、家族が1人1人消えていくホラーが発生するわけです

まとめ:

  • コードポイント: Unicode的に1文字として割り当てられてる21ビットの数値
  • エンコード
    • UTF-16: 2バイトですべての文字を表せると思っていた時代の名残。16ビットで収まる範囲はコードポイントそのまま、収まらない範囲は2文字分(4バイト)使って表現(サロゲートペア)
    • UTF-8: ASCII文字がASCIIコードのまま扱えるように、1~4バイトになるように符号化する方式
  • 書記素クラスター: 表示上1文字になって、選択や削除の際にはまとめて処理されるべき単位

プログラミング言語のString

Java (1995年リリース)の辺りから、新しめのプログラミング言語は大体Unicodeを前提としています。 ソースコードは、UTF-8で保存しておけば日本語とかも識別子(変数名とかクラス名とか)に使えます。 String型は標準でUnicodeを扱えたりします。

とはいえ、Unicode自体が紆余曲折あったので、プログラミング言語も紆余曲折振り回されます。

JavaやC#はString型がUTF-16だったりします。サロゲートペアの処理が結構面倒。 Unicodeがまだ2バイト固定長を夢見ていたころの世代の言語。 UTF-16文字からUnicodeコードポイントを得るためのAPIはあるんで何とかできはしますが。

Goなんかは文字列はUTF-8だそうです。

Swiftは、最近できただけあって、結構頑張っています。 内部的にUTF-8かUTF-16を問わず(String型が内部に弁別用のフラグをもってて、適宜デコードしてくれてる)。 API的にも、String型が、以下のようなメンバー(正確にはextension)を持っています。

  • characters: 書記素クラスターを列挙
  • unicodeScalars: Unicodeコードポイントを列挙
  • utf16: UTF-16エンコードした2バイトを列挙
  • utf8: UTF-8エンコードしたバイト列を列挙

まあ、ちゃんとしてるように見えて、Unicodeの仕様追加に対してちょっと危ういんですが…(後述)

プログラミング言語の識別子

一方で、ソースコード自体の話。特に、識別子に使える文字の話。

まともな神経をしてる言語は、Unicodeカテゴリーで識別子に使える文字を縛っています。 非常に大まかにいうと、たいていの言語で識別子に使えるのはLetterのみ。 記号は使えないように絞ってあります。 逆に、Letterなら大抵なんでも。日本語だろうが漢字だろうが使えます。

C# とサロゲートペア

ちなみに… 現状の C# はサロゲートペア使えません… 変数名に𩸽とか𠮷とかが使えない状態です。 つい最近まで仕様書に「Unicode Standard, Version 3.0」(サロゲートペアなんてものがなかった時代の仕様)って文字が入ってたくらいでして。 今は、「The Roslyn compilers depend on the underlying platform for their Unicode behavior」(プラットフォームによる。Unicode Standardの変更の影響を受ける)に仕様の文面も変更されることになってるんですが、サロゲートペアへの対応はまだ。

最近、中国人っぽい人から不具合報告を受けています。 C#チームの応答を見るに、修正入って使えるようになるかも。 まあ、仕様書的にサロゲートペアが使えてもおかしくなさそうな記述になってる今、 対応していないのはおかしいはずですんで。

Swiftの攻めっぷり

一方で、Swiftのやんちゃっぷりは凄くて… Swiftリリース時点でも、「絵文字が使える!」ってアピールされてたのは結構皆さん覚えてらっしゃるかと思えますが。 調べてみたら、絵文字どころかほんとに何でも使える。 個人的にツボったのは以下の2つのネタコード。

  • 0~9の和は1023 … 数字に見えますけど、「Mathematical Alphanumeric Symbols」って領域にある記号を使っています
  • 不可視識別子 … これも数式用の記号。変数xとyを、xyと並べたときに、それが何を意味するか(掛け算か、足し算か、関数適用か、添え字を並べただけか)を判別するために使います

バグでも実装ミスでもなく、仕様。 いいのか悪いのか… まあ、Letterに限定してもろくでもないコードは書けるんで、程度の問題なんですが。

Unicodeの変更/OSの対応状況に振り回される

Unicode、今もうバージョン9とかなんですっけ。基本は文字の追加だけなので、そんなに毎度毎度は問題を起こさないんですが…時々…

中黒のカテゴリー

有名な問題として、「・」(U+30FB、中黒)の扱いが変わったというやつがありまして。 中黒って、日本語的にはハイフンのような使い方をします。2つの単語を並べるときに区切り文字として使う。 ところが、間違えて、「ー」長音記号と同じカテゴリー(Pcカテゴリー)を割り当てられていたという過去があります。 これを、Unicode 4.1だか辺りで、ハイフン扱い(Poカテゴリー)に修正したそうです。

長音と同じであれば、たいていの言語で識別子に使えます。JavaやC#では、昔は中黒を識別子に使えていました。 それが、JavaはJava 7で、C#はC# 6で、いきなり識別子として使えなくなります。 破壊的変更に対してかなり保守的な言語であるJavaとC#が立て続けに破壊的変更を起こしたという。 言語仕様には全く変化がなく、ただ、使っているUnicodeのバージョンが上がったというのが原因になります。

絵文字の肌トーンと書記素クラスター

割かし最近の追加と言えば肌トーン。「絵文字とカラーフォントがパンドラの箱開けちゃった」と有名なあれ。

「なぜ白い肌の絵文字しかない」とかいう政治的な地雷を踏みぬいた結果、「Emoji Skin Tone Modifiers」という補助文字が作られて、 絵文字の後ろにくっつけると絵文字の肌色が変化するというもの。

iOS 8.3で対応したんでしたっけ。 それ以前は、肌トーンは「肌色の四角形」で描画されていました。顔文字と並べると、顔文字+四角形の2文字が表示されます。 以降は、ちゃんと1文字の顔文字で肌の色が変わります。

ここで、書記素クラスターの話に戻ります。 表示上1文字として描画されるべきものが書記素クラスター。 肌トーンはどうなるかというと、Unicodeコンソーシアムのレポートによれば「単一の書記素クラスターとして扱うべき」とは書かれています。 が、まあ、あくまでそれは、肌トーンに対応したOS上での話。 iOS 8.3より前なら2文字として表示されているわけだし、以降なら1文字扱いしてほしい。

これ、Swiftにとっては、characters(書記素クラスターを列挙)の結果が変わることになります。 現状は変更はされていなくて、肌色指定付きの顔文字のcharacters.countを調べると、2文字扱いされます。

そして、公式フォーラムでも問題に上がってるみたい。

議題は、要するに、

  • characters.countの数え方はプラットフォームに依るべきか
  • プラットフォームに依るべきとすると、プラットフォームごとにcharactersの挙動が変わることになるけど、それはありか
  • 挙動が変わるのを良しとしない場合、プラットフォーム依存な、ちゃんと表示されてる文字数を取る手段はあるか

という話。

もしかして、書記素クラスターは人類が手を出すにはまだ早すぎたのではないか…

サロゲートペア対応C#

何で今こんなこと調べてたかというと、ちょっと、C#コンパイラーいじってみるのの題材として、C#のサロゲートペア対応とかやろうかと思ってまして。

.NET Fringe Japanで「言語機能の作り方」って感じの話をする予定で、その一環。 実際のセッション内容としては、「うかつに独自機能とか考えるんじゃないぞ」という予防接種が半分、「それでもコンパイラーに手を入れたいっていうならデモくらい見せるけど…」みたいな内容が半分の予定で、この後半の内容になります。

どうせやるなら、多少芽のある話をやろうかと思った結果がサロゲートペア対応。 非ASCII文字の話は、日本人か中国人が率先してやらないと全然実装されないですからねぇ。

で、久々にUnicodeの状況とか、C#以外も含めたプログラミング言語の仕様とかを漁ってみてた結果が本日のブログ。 「絵文字はともかく、𩸽くらいは使えてしかるべきよなぁ」とか考えてたら、 ちょうど同じタイミングでRoslynリポジトリ内でその話題(ただし中国語)が出てたり。 Swiftがどこまで緩いの調べてたら想像をはるかに超えてやんちゃだったり。


ピックアップ Roslyn 9/6: defaultの型推論

$
0
0

なんか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されました。

参考:

ピックアップ 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メタプログラミング】ValueChangedGanerator

$
0
0

多少やりすぎ感があって大々的に推すかどうかは迷っているものの…

.NET Compiler Platform (コードネーム Roslyn)を使ったメタプログラミングというか、結構なコード生成ツールを作ってしまっていたり。

今度やる勉強会で.NET Compiler Platformの話をするのと、一度作ってしまえば仕事でも活用できそうだったので。

生成ツールの概要

要するに、たびたび出てくる「INotifyPropertyChangedの実装めんどくさい問題」に対する、Roslyn使った解決方法の1つ。

INotifyPropertyChanged実装はめんどくさいものの、用途が限られてる(ほとんどの場合、クライアント開発者にしか使われない。サーバー側でもINotifyPropertyChanged自体は使われることあるけども、そっちは完全に自動生成の中に隠れてたり)のと、実装方法がたまーにカスタマイズしたいことがあって、C#言語構文としてコンパイラーが何か補助するような機能は作りにくいものです。

で、こういうめんどくさいくせに「用途限定」「実装に幅あり」なものに対して有効な解決策がメタプログラミングなわけですが。最近、Roslynを使ったコード生成にだいぶ慣れてきてしまい、作ってみたのがこのValueChangedGanerator

今回僕が作った実装以外にも、つい最近、ノイエさんNotifyPropertyChangedGeneratorというやつも作っていたり。というか、これがこっち作るきっかけ。NotifyPropertyChangedGeneratorこっちの違い(pull-req 送るとかでなく別実装した理由)は:

  • プロパティ自身の書き換えでなく、コード生成元にはノータッチで、完全に別の場所にコードを生成
  • コード生成先を、同一ファイル内の #region ではなく、別ファイルに分離
  • SetProperty()まではコード生成せず、こいつは元々ある想定

の3点です。

生成方式

  • 生成元には、NotifyRecordという名前で入れ子になった構造体を定義します。
  • この名前の入れ子構造体があると、Quick Action(電球マーク)が出ます
  • 「Generate properties」を選択すると、コードが生成されます。

スクリーンショット

例えば、以下のようなコードが Point.cs と言う名前であったとします。

partial class Point : BindableBase
{
    struct NotifyRecord
    {
        public int X;
        public int Y;
        public int Z => X * Y;

        /// <summary>
        /// Name.
        /// </summary>
        public string Name;
    }
}

このファイル自体も、Pointクラスに対してpartial修飾子がつく修正がかかります。

そして、Point.ValueChanged.cs という名前で、以下のようなコードが生成されます。

using System.ComponentModel;

partial class Point
{
    private NotifyRecord _value;

    public int X { get { return _value.X; } set { SetProperty(ref _value.X, value, XProperty); OnPropertyChanged(ZProperty); } }
    private static readonly PropertyChangedEventArgs XProperty = new PropertyChangedEventArgs(nameof(X));
    public int Y { get { return _value.Y; } set { SetProperty(ref _value.Y, value, YProperty); OnPropertyChanged(ZProperty); } }
    private static readonly PropertyChangedEventArgs YProperty = new PropertyChangedEventArgs(nameof(Y));

    /// <summary>
    /// Name.
    /// </summary>
    public string Name { get { return _value.Name; } set { SetProperty(ref _value.Name, value, NameProperty); } }
    private static readonly PropertyChangedEventArgs NameProperty = new PropertyChangedEventArgs(nameof(Name));
    public int Z => _value.Z;
    private static readonly PropertyChangedEventArgs ZProperty = new PropertyChangedEventArgs(nameof(Z));
}

SetPropertyOnPropertyChangedの実装は、元のクラス(今回の場合Point)が持っている前提でコード生成します。今回は、基底クラスで実装している想定。その基底クラスBindableBaseは以下のような実装になっています。

using System.Collections.Generic;
using System.ComponentModel;
using System.Runtime.CompilerServices;

class BindableBase : INotifyPropertyChanged
{
    #region INotifyPropertyChanged

    public event PropertyChangedEventHandler PropertyChanged;

    protected void OnPropertyChanged(PropertyChangedEventArgs args) => PropertyChanged?.Invoke(this, args);

    protected void OnPropertyChanged([CallerMemberName] string propertyName = null) => OnPropertyChanged(new PropertyChangedEventArgs(propertyName));

    protected void SetProperty<T>(ref T storage, T value, PropertyChangedEventArgs args)
    {
        if (!EqualityComparer<T>.Default.Equals(storage, value))
        {
            storage = value;
            OnPropertyChanged(args);
        }
    }

    protected void SetProperty<T>(ref T storage, T value, [CallerMemberName] string propertyName = null) => SetProperty(ref storage, value, new PropertyChangedEventArgs(propertyName));

    #endregion
}

もちろん、このためにだけに継承を使うのは嫌という話もあります。それはまた別の方法で解決しようかなぁと(未実装。Test-Firstで先に生成前後のサンプルだけあり)。そっちもやりすぎ感あふれてて悩ましいですが…

実装の理由

生成元は正規のC#文法

「似て非なる何か」、「亜種」を作らないように、生成元のコードは通常のC#の文法に収まるようにしています。

なので、多少冗長な感じは残ります。今回の実装だと、一段階入れ子にした構造体に元定義を書くので、3行(struct NotifyRecord, {, })と、1インデント分の無駄はあります。

生成は静的に/生成結果がC#コードとして見れる

デバッグ上、生成結果のC#コードが見れるというのは非常に重要。

他の言語で見られるような、動的な処理とか、ビルド後コード挿入でやるようなメタプログラミングは、だいたいデバッグできなくて泣きそうになるので。

.NET Compiler Platformを使ったコード生成でやっているので、この条件は自然と満たします。

生成元と生成結果が分かれるように

生成の前後のコードが混ざると、再生成時にどこを書き替えたらいいのか分からなかったり、機械生成の部分に手を入れてしまって再生成で消してしまったりするので。

今回は、元は入れ子の構造体なので、生成元と生成結果がかち合うことはない。 また、partialにして別ファイルに分離。

docコメントも付くように

生成結果がpublicなプロパティなので、ここにdocコメントが付いていてほしい。でないと、「doc XMLを出力する」設定でビルドした時に警告が大量に出てしまうので。

今回は、生成元のフィールドに付けたdocコメントをそのまま移植する実装になっています。

利用方法

github公開してるソースコードの他に、パッケージ化してギャラリーに置いてあります。

.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メタプログラミング】ValueChangedGanerator

$
0
0

多少やりすぎ感があって大々的に推すかどうかは迷っているものの…

.NET Compiler Platform (コードネーム Roslyn)を使ったメタプログラミングというか、結構なコード生成ツールを作ってしまっていたり。

今度やる勉強会で.NET Compiler Platformの話をするのと、一度作ってしまえば仕事でも活用できそうだったので。

生成ツールの概要

要するに、たびたび出てくる「INotifyPropertyChangedの実装めんどくさい問題」に対する、Roslyn使った解決方法の1つ。

INotifyPropertyChanged実装はめんどくさいものの、用途が限られてる(ほとんどの場合、クライアント開発者にしか使われない。サーバー側でもINotifyPropertyChanged自体は使われることあるけども、そっちは完全に自動生成の中に隠れてたり)のと、実装方法がたまーにカスタマイズしたいことがあって、C#言語構文としてコンパイラーが何か補助するような機能は作りにくいものです。

で、こういうめんどくさいくせに「用途限定」「実装に幅あり」なものに対して有効な解決策がメタプログラミングなわけですが。最近、Roslynを使ったコード生成にだいぶ慣れてきてしまい、作ってみたのがこのValueChangedGanerator

今回僕が作った実装以外にも、つい最近、ノイエさんNotifyPropertyChangedGeneratorというやつも作っていたり。というか、これがこっち作るきっかけ。NotifyPropertyChangedGeneratorこっちの違い(pull-req 送るとかでなく別実装した理由)は:

  • プロパティ自身の書き換えでなく、コード生成元にはノータッチで、完全に別の場所にコードを生成
  • コード生成先を、同一ファイル内の #region ではなく、別ファイルに分離
  • SetProperty()まではコード生成せず、こいつは元々ある想定

の3点です。

生成方式

  • 生成元には、NotifyRecordという名前で入れ子になった構造体を定義します。
  • この名前の入れ子構造体があると、Quick Action(電球マーク)が出ます
  • 「Generate properties」を選択すると、コードが生成されます。

スクリーンショット

例えば、以下のようなコードが Point.cs と言う名前であったとします。

partial class Point : BindableBase
{
    struct NotifyRecord
    {
        public int X;
        public int Y;
        public int Z => X * Y;

        /// <summary>
        /// Name.
        /// </summary>
        public string Name;
    }
}

このファイル自体も、Pointクラスに対してpartial修飾子がつく修正がかかります。

そして、Point.ValueChanged.cs という名前で、以下のようなコードが生成されます。

using System.ComponentModel;

partial class Point
{
    private NotifyRecord _value;

    public int X { get { return _value.X; } set { SetProperty(ref _value.X, value, XProperty); OnPropertyChanged(ZProperty); } }
    private static readonly PropertyChangedEventArgs XProperty = new PropertyChangedEventArgs(nameof(X));
    public int Y { get { return _value.Y; } set { SetProperty(ref _value.Y, value, YProperty); OnPropertyChanged(ZProperty); } }
    private static readonly PropertyChangedEventArgs YProperty = new PropertyChangedEventArgs(nameof(Y));

    /// <summary>
    /// Name.
    /// </summary>
    public string Name { get { return _value.Name; } set { SetProperty(ref _value.Name, value, NameProperty); } }
    private static readonly PropertyChangedEventArgs NameProperty = new PropertyChangedEventArgs(nameof(Name));
    public int Z => _value.Z;
    private static readonly PropertyChangedEventArgs ZProperty = new PropertyChangedEventArgs(nameof(Z));
}

SetPropertyOnPropertyChangedの実装は、元のクラス(今回の場合Point)が持っている前提でコード生成します。今回は、基底クラスで実装している想定。その基底クラスBindableBaseは以下のような実装になっています。

using System.Collections.Generic;
using System.ComponentModel;
using System.Runtime.CompilerServices;

class BindableBase : INotifyPropertyChanged
{
    #region INotifyPropertyChanged

    public event PropertyChangedEventHandler PropertyChanged;

    protected void OnPropertyChanged(PropertyChangedEventArgs args) => PropertyChanged?.Invoke(this, args);

    protected void OnPropertyChanged([CallerMemberName] string propertyName = null) => OnPropertyChanged(new PropertyChangedEventArgs(propertyName));

    protected void SetProperty<T>(ref T storage, T value, PropertyChangedEventArgs args)
    {
        if (!EqualityComparer<T>.Default.Equals(storage, value))
        {
            storage = value;
            OnPropertyChanged(args);
        }
    }

    protected void SetProperty<T>(ref T storage, T value, [CallerMemberName] string propertyName = null) => SetProperty(ref storage, value, new PropertyChangedEventArgs(propertyName));

    #endregion
}

もちろん、このためにだけに継承を使うのは嫌という話もあります。それはまた別の方法で解決しようかなぁと(未実装。Test-Firstで先に生成前後のサンプルだけあり)。そっちもやりすぎ感あふれてて悩ましいですが…

実装の理由

生成元は正規のC#文法

「似て非なる何か」、「亜種」を作らないように、生成元のコードは通常のC#の文法に収まるようにしています。

なので、多少冗長な感じは残ります。今回の実装だと、一段階入れ子にした構造体に元定義を書くので、3行(struct NotifyRecord, {, })と、1インデント分の無駄はあります。

生成は静的に/生成結果がC#コードとして見れる

デバッグ上、生成結果のC#コードが見れるというのは非常に重要。

他の言語で見られるような、動的な処理とか、ビルド後コード挿入でやるようなメタプログラミングは、だいたいデバッグできなくて泣きそうになるので。

.NET Compiler Platformを使ったコード生成でやっているので、この条件は自然と満たします。

生成元と生成結果が分かれるように

生成の前後のコードが混ざると、再生成時にどこを書き替えたらいいのか分からなかったり、機械生成の部分に手を入れてしまって再生成で消してしまったりするので。

今回は、元は入れ子の構造体なので、生成元と生成結果がかち合うことはない。 また、partialにして別ファイルに分離。

docコメントも付くように

生成結果がpublicなプロパティなので、ここにdocコメントが付いていてほしい。でないと、「doc XMLを出力する」設定でビルドした時に警告が大量に出てしまうので。

今回は、生成元のフィールドに付けたdocコメントをそのまま移植する実装になっています。

利用方法

github公開してるソースコードの他に、パッケージ化してギャラリーに置いてあります。

Viewing all 483 articles
Browse latest View live