エイプリルフールなので、ポエムを書きます。
Kotlin が仮に .NET で動いたらのお話し。全体のソースコードはこちら
背景
WPF でアプリを書く際、ライブラリを使わない場合は ViewModel のクラスには毎回のように INotifyPropertyChanged
インターフェースの実装を書いていたので、面倒でした。
ところ変わって、 Kotlin の Delegated Properties という構文は衝撃的で、面白い何かに応用できそうだと思っています。
他に継承させたいクラスがある場合を考えると、 INotifyPropertyChanged
は必ずインターフェースとして実装したいですし、それは C# の他のライブラリでもできるので意味がないです。
結果
解説の前にまずは結果を。
4 | class MainViewModel : NotifyPropertyChanged { |
17 | fun main(args: Array<String>) { |
実行結果は以下の通りです。
1 | Notified AnotherProperty [MainViewModel@246b179d] |
解説
+=
によるイベント追加のからくり
Kotlin では特定の名前の関数を operator
で修飾すると、演算子の記号としてその関数を呼び出せるようになる。
このコードの EventHandler
クラスのように plusAssign()
関数を記述することで、 +=
が使えるようになる。なお、下の invoke()
関数も同じで、メソッド呼び出しのように呼び出せる。
fullName
プロパティの後ろの by
とは
これが Kotlin の Delegated Properties というもので、 getter と setter の処理を他のオブジェクトに任せることができます。
このコードの PropertyChangedDelegate
クラスでは、 setValue()
関数内で raisePropertyChanged()
関数によって、イベントを発行しています。つまり、 C# の getter と setter に書いていたボイラープレートを、この機能を使って楽に書いています。
ちなみに、カッコ内は初期値とし、値があることを強制させました。( Kotlin は null
かそうでないかに厳しいので)
なぜインターフェースなのに NotifyPropertyChanged
がインスタンスを持っている(ように見える)のか
実は、 Kotlin はインターフェースの中にも状態のない実装なら書けます。
- インターフェースのプロパティは Delegated Property にできないが、メソッドの実行ならできる。
- インターフェース内でも
this
が使えてしまうので、実際のインスタンスに固有の番号も取得できてしまう。 - マップなり辞書を作成して、すべての
NotifyPropertyChanged
インターフェースを実装するインスタンスの中のpropertyChanged()
関数を通して情報を取得する。
これを組み合わせた NotifyPropertyChanged
インターフェースを作ることで、少なくとも見た目は NotifyPropertyChanged
インターフェースを継承するだけでいろいろ使えるようになりました。
感想
こういうのはパフォーマンスの評価も必要だと思うんですけど、ちょっと面倒なのでしてないです。
あとは、 VM のインスタンスが消えた後も NotifyPropertyChanged
インターフェースでのマップ・辞書のインスタンスは残ってることになるので、そのあたりも要検討です。
Kotlin の Delegated Properties については他サイトにわかりやすい解説があるので、そちらをオススメします。