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

http://ufcpp.net/

Archive for 10月 2014

イベントのメンバー名

leave a comment »

背景: イベント使いにくい

.NET 4が出て以来、「イベントを最初に1回だけ受け取りたい」みたいな要件では、Taskクラスを返すようにしているわけですが。

ボタンを1回押したらページ遷移しちゃうから、1回きりしか受け取る必要がない。Viewの債務としては「遷移用のボタンが押されるまで待つTaskを返す」(状態遷移のトリガーになる)だけにして、実際のページ遷移(状態遷移の管理)はnavigatorみたいなやつが外に別にいて、そっちに任せたい、みたいな使い方をしていまして。

Rxを使えば割と簡単には書けたりします。

public event EventHandler<Point> Click;

public Task<EventPattern<Point>> AwaitClick()

{

    return Observable.FromEventPattern<Point>(this, “Click”).FirstAsync().ToTask();

}

といっても、”Click” の部分が文字列リテラルになっちゃってるのを見て察しがつく通り、これは内部的にはリフレクションを使ってやっています。もちろん効率悪い。

eventを(add/removeメソッドを取り出せる形で)引数として使えればこんな面倒起きないんですけどね。C#に、以下のような構文でも入ってくれれば。

public static Task<TEventArg> FirstAsync<TEventArg>(event EventHandler<TEventArg> e)

{

     var tsc = new TaskCompletionSource<TEventArg>();

    EventHandler<TEventArg> handler = null;

    handler = (sender, arg) =>

    {

         e.Remove(handler);

         tsc.SetResult(arg);

     };

     e.Add(handler);

     return tsc.Task;

}

まあ、あんまり実現しなさそうな機能要望ですが。

てことで、C#のevent構文使うのいい加減やめて(Rxが出たころから言われてはいるものの、今考えてもやっぱりやめたい)、最初からIObservable<T>使う方がいいんじゃないかと思っていたり。

次の問題: メンバーが3つに

で、せっかくRxを使って書くんなら、以下のようにしたい。

// Subscribe(イベントとして見るなら、要はAdd/Remove) public にしたい(ハンドラーの登録口は当然 public)

public IObservable<Point> Click { get { return _click; } }

// OnNext(イベントとして見るなら、要はInvoke) protected にしたい(実際にイベントを起こすのは派生クラスでやりたい)

protected IObserver<Point> _Click { get { return _click; } }

// 実体は private にしたい。初期化だけでいいんだけども。

private readonly Subject<Point> _click = new Subject<Point>();

Click, _Click, _clickとかいう名前が並んでるのとか、自分で書いてても思うけども、かなり邪悪。とはいえ、この3つって、同じ1つのイベントを、異なる3つの側面から見てるだけなので、本質的には1つにしたいんですよね。

event構文の場合、

public event EventHandler<Point> Click;

って書いたら、

  • 外側から見えるイベント(ハンドラー登録口)としてのClick (public)
  • 内側から呼べるデリゲートとしてのClick (private)

の2つが、同じ名前Eで作られるわけで (同じ名前で2つの側面があることで、それはそれで初心者への説明に困る機能だったりはしますが)、これと同じノリの同名別側面が使いたい。

C# vNextで多少マシになるとして

C# vNextで、get-only auto-propertyが入るので、多少はマシにできそうな感じはするんですが…

// 1個だけなら、C# vNext get-only auto-property とその初期化子で解決できるんだけども。

public IObservable<Point> Click { get; } = new Subject<Point>();

「初期化子を使ってreadonlyに初期化がしたいだけ」なのであれば、これで解決します。問題は、public IObservableなClickの1個だけじゃないこと。

// この場合、問題は、2個、別インターフェイスで受けたいということ。

public IObservable<Point> Click { get; } = new Subject<Point>();

// ↓これじゃだめ。と同じ Subject インスタンスを参照したい。

protected IObserver<Point> _Click { get; } = new Subject<Point>();

あるいは、ダウンキャストでごまかせなくもないんですが…

public IObservable<Point> Click { get; } = new Subject<Point>();

// こう書けなくはないけども、ダウンキャストが結構みっともない

protected IObserver<Point> _Click => Click as IObserver<Point>;

そもそも、これでも、3つが2つに減っただけで、同じものの別側面に対する名前付けで悩むのは一緒。

結局どうしよう

で、今回僕はあきらめて、最初に挙げた邪悪なClick, _Click, _clickを使うことにしてしまったわけですが。

他だと、publicにInvokeされてしまうことを認める方向、つまり、同様にRxを使うなら、

public Subject<Point> Click { get; } = new Subject<Point>();

こうしてしまう(Rxを使わない場合でも、Subscribe, OnNext両方相当のものを持ったクラスをpublicにしてしまう)方向で妥協する人なんかも多い(Unityゲームエンジンがまさにそれ。UnityEventとかいう型。あと、F#のイベントもそんなノリ)。外からInvokeできるのは、イベントの偽装(テストとか自動化)がしやすい反面、悪用・誤用(単一スレッド動作しか保証されないUIイベントなんかを別スレッドから起こせたり)し放題なので、微妙は微妙。

という感じで悩んでいたりします。というか、こんな話が(C#もリリースされてから10年以上経っても)今になってまた悩む程度には、ずっと悩ましいんですが。どうしよう。

広告

Written by ufcpp

2014年10月27日 at 23:20

カテゴリー: C#

VS 2014 CTP 4

leave a comment »

Visual Studio “14” CTP 4 がリリースされたそうです

主な更新は ASP.NET vNext がらみで、例えば「VS上でのデザイン時、ビルドを実行する前から常にコンパイルされた状態にすることでデバッグ実行のパフォーマンスをよくする」みたいなのとかがリリースノートに書かれています。それと、RyuJIT(新しい64bit .NET JITコンパイラー)が正式搭載されたみたい。

まあ、その辺りはいいとして。個人的な興味はC#コンパイラーの進捗なわけですが。

先日の記事、C# vNext、そろそろ仕様fixで書きましたが、つい最近以下のような決定がありました。

  • out (「次」には入らず、さらにその次行き。今のCTPに仮実装が入っているけども、これから抜く)
    • primary constructors
    • declaration expressions
  • in (現在のCTPには実装されていないけども、「次」までには入れる)
    • string interpolation

が、これはCTP 4には反映されていないみたい。primary constructorsとかは使えるし、string interpolationは使えない。

あと、たぶん、CTP 3からの差分だと、

  • index付き初期化子(var dic = new Dictionary<string, int> { [“one”] = 1, [“two”] = 2 }; みたいなの)
  • 構造体のパラメーターなしのコンストラクター (struct X { public X() { } } とか struct X { int x = 10; } みたいなの)

が書けるようになってます。

それから、ふと気づいたんですけども、ASP.NET vNext付属の(kproj方式の)コンソールアプリと、これまでのコンソールアプリで挙動が違うみたい。以下のような。

全く同じソースコードのコピペなんですが、左から順に、

  • 既存コンソールアプリ(<LangVersion>experimental</LangVersion>指定あり) … エラーなし
  • ASP.NET vNext付属(kproj)コンソールアプリ … エラーなし
  • 既存コンソールアプリ(LangVersion指定なし) … 引数なしコンストラクターがエラーに

というもの。kprojのやつには既定でexperimentalオプションが指定されているのか、それとも使っているRoslynのバージョンが違うのか。

ちなみに、書いたコードはこんなの → https://gist.github.com/ufcpp/51bbca95d4fe76a5f0cf

Written by ufcpp

2014年10月7日 at 22:53

カテゴリー: C#

C# vNext、そろそろ仕様fix

with one comment

こないだtwitterでつぶやいたので満足してたけども、もうちょっとちゃんとした説明をしておこうかと思って。

10/1に、こんな話が。

要約すると、

  • そろそろC#/VBの次期バージョンに入れる機能セットをfixしないと行けない
  • 工期的な都合で、以下の2つの機能は「次」からは外したい(さらにその次に回したい)
    • primary constructors
    • declaration expressions
  • 代わりに、現CTP (CTP 3)には入っていない string interpolation は「次」に入れることが決まった

というものです。個人的な感想としては、primary constructorsはほんと今すぐでも欲しいので多少残念なものの… 一応、外れた理由が外れた理由なので、期待して待とうという感じ。外れた理由は、

They are both characterized by having large amounts of downstream work still remaining. They are also features that we see as the potential beginning of a bigger story further down the line: primary constructors could grow up to become a full-blown record feature, and declaration expressions would form the corner stone of pattern matching and deconstruction facilities. Now, those features will all be considered together for a later release. As a silver lining we then get to design this continuum holistically, rather than in steps that might tie our hands unduly in a later phase.

大まかに要点訳すと、

これらは両方ともまだまだやるべき作業が大量に残っている。また、より大きなストーリー(後述するrecord/pattern matchingのこと)の開始地点でもあった。primary constructorsはrecordに、declaration expressionsはpattern matchingとdeconstructionになる。これらの機能はもっと後のリリースに入れる。段階的な投入は不要な面倒になる。

というようなもの。

ここで出てきている「より大きなストーリー」、recordとpattern matchingについては、こないだブログで少し触れたけども、関数型言語でよく見るような型のパターン マッチング仕様。かなり便利そうなものなので、この辺りに生まれ変わるというんなら、「次の次」まで我慢しましょうか…

それよりも、前向きにとらえるなら、C# 6.0/VB 12が「final stage」入ったって点は嬉しかったりします。今決まっているような便利な機能は、正式に使えるようになる日が近づいてきたということのはずなので。正式にvNextに入りそうな機能の一覧は以下のページを参照。Done/Planned (もしかしたら Maybe も)になっているものがそう。

Written by ufcpp

2014年10月5日 at 23:55

カテゴリー: 未分類