Bitbucket にプライベートな git を作る方法

月7$ 払って github にプライベートレポジトリを作ればいいんだけど、その前にお試し的に Bitbucket で作って運用してみるテスト。大抵の場合は、面倒なので public で github で作ってしまえばいいのだが、ちょっと内部的なのだと非公開で作りたいときもあるので、メモとして。

既存のものからアップロードしようとしたり、あれこれやったんだけど、なんかうまくいかない。したかがない(?)ので、Bitbucket の手順に従ってちまちまやるのが一番手っ取り早いです。

image

リポジトリの作成から、新規リポジトリの作成

image

新しいリポジトリが作成できたら、コマンドラインの「私はゼロからスタートします」をコピペしながら実行する。「SourceTreeにクローン」のほうは、Firefox で動かなかったので

image

 

mkdir /path/to/your/project
cd /path/to/your/project
git init
git remote add origin https://moonmile@bitbucket.org/moonmile/sample.git

 

git init と git remote を使って、新規作成したレポジトリをローカルの PC に作れば ok.

あとは、ローカル PC で、Visual Studio でプロジェクトを作るとか、他に保存してあるプロジェクトをコピーするとかして、commit, push すればよい。

他の PC や Mac では git clone すればよいので、これでコードの同期が簡単にできる。

カテゴリー: 開発 | Bitbucket にプライベートな git を作る方法 はコメントを受け付けていません

MVVM+MVCパターンを大規模システムにスケールする

大規模プロジェクトで MVVM を導入するときの注意 | Moonmile Solutions Blog をもう少し突っ込ん考えてみます。大規模といっても、みずほみたいにできあがらなくて破綻したパターンもあれば、特許庁の場合のように設計時で破綻した場合もあれば、豊洲のように作ってからそもそも破綻しているのもあるわけで、常々プロジェクトの成功率は30%程度なので、大抵は破綻します。まあ、途中でプロジェクトとしてうまく失敗させる(一気に解散する)か、うまくプロジェクトを軟着陸できずにだらだとお金をつぎ込んで「失敗」させることがことができず終わる二百三高地になるやもしれません。

さて、それなりの資源(人と金)をつぎ込んでペイができるかどうかのバランスを保つ場合(公共事業の場合は、バランスを保たなくてもよい場合もあるので別)、「失敗が許されない」パターンがあります。しかし、失敗が許されないの対偶を人は「必ず成功する/させる」と受け取りますが、実は「全面的に失敗という状態にならければよい」わけで、部分的に成功であればつぎ込んだモノへの回収ができます。端的に言えば、工事進行基準になるのですが、実際ソフトウェアの開発工程に工事進行基準を持ち込んで、プロジェクトが半分で頓挫したときにそのプロジェクトには半分の価値があるのか?というと怪しいところがあります。と言いますか、大抵の場合、無いです。例えば、脱サラでよくやる飲食店のパターンは、初期投資でキッチンを購入したりするのですが、途中で潰れるとキッチンを下取りできるので、そこそこペイができます。が、ソフトウェア開発の場合、注力するもののほとんどが「人件費」なので、途中で頓挫したからといって、それを中古として出すことはできません。まあ、頓挫したサイトを売り買いするパターンもあるけど(会員データ込みとか?)大抵の場合は、工事進行基準とかEVのように価値が右上がりになるのではなく、プロジェクトの終了直前(完了直前)に一気に価値があがるのが普通です。ですが、長期のプロジェクトは人数が多い大規模なプロジェクトになると、最後の一気にあがる価値の時期に達しない場合は、まるごと価値がなくなるということなり、EV的(アーンドヴァリュー的)になだらかな価値が生み出されているという訳ではありません。

image

発注側としては、まるで既製品の車を買うように完成した品を即使いたいわけですから、納品直前に支払いをするほうが得なので、↑の図は望ましい形になります。支払を遅らせるという点で、資金活用もできますからね。ですが、請負側としては、支払いは納品により一括だし、途中の破綻のリスクを負わなければならず、たまったものではないので、プロジェクト工程を分けて分割支払いにします。

image

こんな風に段階的にリリースして、価値を発注側に提供して支払いを得ます。プロジェクト工程でいれば、要件定義とか設計工程とか製造工程とかいうやつですね。実際、それぞれの工程に確かな「価値」があるかどうかはわかりませんが、価値があるかのように振る舞うわけです。それぞれの工程が別の人物/会社にて行われているときには、いわゆる「作り逃げ」の手が使えるわけで、豊洲とか特許庁のあれはそのパターンです。その工程の本来の「価値」とは何かを忘れ去っています。

さて、段階的なリリースをプロジェクトの各工程にあてはめましたが、アジャイル開発のイテレーション開発の方法では、この段階的なリリースは「完成品」をしてひとつのプロジェクトとしています。スクラムで言えば、2週間ごとにリリースするとか、コアな部分だけ先に作って後から顧客の要望を取り入れる(あるいは削っていく)という方法がありますね。そのような、短期的なリリース(分割リリース)をもって、全体のプロジェクトを長期に引き延ばせば、マイグレーション開発というスタイルになるだろうし、そこは保守/運用を絡めて継続的に開発していくというスタイルを取ります。勿論、この継続したスタイルや段階リリースがすべてのプロジェクトに当てはまるわけではありません。近いところでは、Apple の Map の惨事は、ひょっとして「段階リリースの一部」といえば許せるかもしれませんが、製品とみたときはダメですよね。紙の書籍であっても、買ったときは表紙だけあって、あとから中身をちょっとずつ届けますと言われても、表紙が紙の状態では買いません(当たり前)。段階的なリリースあっても、顧客の視点からみて「何らかの形で価値が完成されて」いないと、価値とはみなされませんし、対価を支払う気にはなりません。そうそう、「月刊○○」のように毎月500円ずつ買わせるというパターンは、実は段階的な価値提供の成功例かもしれません。

発注視点で分割する

車のように完成したものしか価値がない場合と違って、実はソフトウェア開発において「分割リリース」は、発注側にとってもメリットがあるものです。必ず、そのプロジェクトが完成すると保障されているとしても(プロジェクトが失敗するリスクを0としたとしても)、発注側にとって5年後に5億円を支払って価値を得るよりも、1年目に1億円を支払って活用したしたときの価値を得たほうが投資率はいいですよね。また、4年後のオリンピックを目標にするならば、4年後に4億円を支払って価値を得るというスタイルよりも、1年目に2億円の支払いをして半分の価値を得たほうが、先行者利益を得られる可能性が高まります。

そんな訳で、長期かつ大規模なプロジェクトの場合、発注側の視点での「分割リリース」が意外と重要になってきます。大抵の場合は、ウォーターフォール開発やイテレーション開発の視点でプロジェクト工程として分けたり、アジャイル開発にして顧客の要望に沿わせたりするのですが、それとは違う視点での「分割」の仕方が必要なことがわかります(そういう視点で、アジャイル開発をしているならば別ですが)。

大規模プロジェクトを発注視点で分割する

5から10名程度がアジャイル開発の基本ならば、人海戦術的にウォーターフォールやPMBOKを活用するのが規模の大きい開発の基本とはなるのでしょうが、もう少し平易に、前段階の時点での分割を考えます。

先に話したように、顧客/発注側の都合を加味してプロジェクトを分割してみると、

  • 投資効果の大きい部分を先行リリースする。
  • 先行リリースされたものを使う場合は、完成品よりも人手がかかる。
  • 先行リリースされたものは不備/不足が多いため、人手で回避する。

ということになります。どこかの Xamarin.Forms のプロジェクトを想像させますが、まあ、あれと似たような感じです。OSS ならば、ひとまず公開してしまって(Xamarin.Forms はクローズドからスタートだけど)、それ自身に価値があるのかどうかを市場評価したうえで、継続的に開発をすすめるか/あるいは撤退するかの判断ができますよね。開発者は無限に時間を使えるわけではないのですから。

これは、多少ポンコツではあっても駅まで荷物を運べる車が便利とか、多少メンテナンス料金がかかったって時間が1/10で済むのであればロボットで自動化させたお菓子の生産ライン、みたいな感じですよね。人は、最初の材料を入れるところと梱包のところを任されていたり、ロボットが動かなくなったときのために張り付くわけです。

そうそう、iPhone のような完成したスタイルを求める場合は、分割リリースのほうが嫌われるわけで、そのあたりは Apple Map の叩かれ具合と Bing Map の叩かれ具合から察することができます。

分割リリースによる多少の不具合/不備を、発注側が受け入れるかわりに、先行投資としての価値を発注側が最終リリースのときよりも多く受け取れる、という価値を発注者側が熟知している、という前提が重要になります。

image

それらを前提にして、発注側視点で分割をしたとき、たとえば MVVM パターンの場合、大規模開発に参画している会社/チームは、View や ViewModel などに特化している訳ではなく、それぞれの業務知識の範囲≒ドメイン内で分割します。

請負視点を加味する

さて、発注視点であれば、投資効果の高い部分からの開発&リリースをすればよいということになります。ということは、設計段階において、いくつかの制限がでてきます。

  • とあるモジュール/ライブラリ/コンポーネントが、先行リリースで独立して動けること
  • とあるモジュール/ライブラリ/コンポーネントが、継続的に改修可能であること
  • とあるモジュール/ライブラリ/コンポーネントが、他システムと連携して動作可能であること

一見、当たり前のように見えますが、最終リリース型のウォーターフォール開発を行ったとき(最終リリース型のアジャイル開発でも同じ)、各コンポーネントは独立して動く必要なく他コンポーネントと密接な連携をしていても構いません。また、最終系までリリースが延期されるので、リリース後の改修を考慮に入れなくてもよいパターンが存在します。それにより、拡張性を無視することによる開発の効率化やコストカットが可能です。また、リリースまで動作が保証されないので、他システム/コンポーネントとの連携動作は最終リリース時まで保留になっていてもよいのです。たとえば、テスト環境できっちり動作することがプロジェクト運行時に保証されていれば、それが途中途中で他コンポーネントと連携させて無理に動作させる必要はありません。いわゆる、糊やにかわ的な部分を作る必要がなくなり、これもコストカットに貢献しています。

このため、多分にしてコスト高になりがちな分割リリースですが、そのコスト高をうわまる先行投資のペイを発注側が求めているならば(あるいは、発注側に認めさせることができれば)、このような分割リリースのための設計は意味があります。

ひとつの方法は「デザイン・ルール」のモジュール化により、各依存関係(設計時の変更工数も含め)を求めて、できるだけ関係の少ない状態にします。この本自体は、アイデアに過ぎず実体は伴わないのですが、私は「アイデア」として利用しています。

MVVM+MVC と TDD を活用する

肝としては、「設計段階で分割を考慮した設計をする」だけなのですが、そのあたりをアーキテクトと呼んだり呼ばなかったりしますが、まあ、少なくとも分割を考慮しない設計をした場合は、分割リリースはできません。継続リリースも困難ですよね。

image

一例を出すと、

  • ブラウザ
  • クライアントアプリ C#
  • サーバーシステム PHP

というシステムの分割の仕方があります。クライアントサイドでは MVVM パターンを使い、サーバーサイドでは MVC パターンを使う。Model はシリアライズ機能を用意しておき、ネットワーク系でデータのやり取りを行う。データベースは、サーバーサイドにあるパターンですね。クライアントは、Windowsデスクトップクライアントと、ブラウザでアクセスするクライアントを用意します。ブラウザからは、jQuery で Web API を調節呼び出してもよいし、別途 CakePHP 側にブラウザ用の View を作ることもできます。

当然のことながら、それぞれのコンポーネントは業務に直結しているわけではないので、各システム(在庫システムとか顧客管理システムとか)に応用が可能だし、実際応用ができます。コンポーネント自体は、独立で動作できるように TDD を用意しておきます。

難点としては、何かの機能を付け加えようとしたときに、クライアントサイドからサーバーサイドの一貫を見直さなければいけません。機能を追加したときに(特に削減したときに)、他のシステムに提供している機能が変化していないかを随時チェックする必要があります。それらを、手作業/人海戦術でやっていては、お金がいくらあっても足りません。何らかの形で自動化を行います。逆に言えば、自動化できないのであれば、そこの機能は常に切り捨てられるものであり、一品ものでしかないということです。

上記のシステム例で言えば、それぞれのコンポーネントのインターフェースが揃った状態で、ひとまず分割リリースの用意ができています。インターフェースしかないので、中身がからっぽだから何も動作はしないけど、でも、分割リリースができる程度の動作はするわけです。つまり、動くものの価値を発生さることが可能です。ほら、さきの EV 的な価値創出に近づいたでしょう。

image

そりゃ、要件定義も大切だけど(機能を絞るとか)設計がボロボロだったら、何もできあがりませんよね…ってな具合です。

横断型の TDD は、コンポーネントごとに TDD を作る方法とは違うので、これはまた別途書きます。あと、連携するコンポーネントが多くなると、先に書いた通り機能を追加する旅にあちこち手を入れる(MVC パターンだったら必ず3箇所に手を入れるとか)になるので、ここも別途手法を変えます。じゃないと手数ばかりかかって余計大変になるので失敗する率があがります。

カテゴリー: 設計 | MVVM+MVCパターンを大規模システムにスケールする はコメントを受け付けていません

アリスは System.Json に出会う

ということで、さっそくダウンロードして、無理矢理 .NET Framework のほうでビルドします。ええ、.NET Core じゃなくて、普通の .NET Framework に持って来るわけですね。

corefx/src/System.Json at master ・ dotnet/corefx
https://github.com/dotnet/corefx/tree/master/src/System.Json

から System.Json の src 以下をごっそり持ってきます。

Visual Studio 2015 でクラスライブラリを作成

実は、そのまま corefx のプロジェクトを持って来て少し変えるだけでいけるかなと思ったのですが、無理でした。

エラー時に System.SR という内部的なリソースにクラスにアクセスしているので、ビルドができません。仕方がないので、以下のようなダミークラスを作ります。

internal sealed class SR
{
    public static string ArgumentException_ArrayMustEndWithBracket { get; internal set; }
    public static object ArgumentException_ExpectedXButGotY { get; internal set; }
    public static object ArgumentException_ExpectedXDiferedAtY { get; internal set; }
    public static string ArgumentException_ExtraCharacters { get; internal set; }
    public static string ArgumentException_ExtraDot { get; internal set; }
    public static string ArgumentException_IncompleteEscapeLiteral { get; internal set; }
    public static string ArgumentException_IncompleteEscapeSequence { get; internal set; }
    public static string ArgumentException_IncompleteExponent { get; internal set; }
    public static string ArgumentException_IncompleteInput { get; internal set; }
    public static string ArgumentException_InvalidLiteralFormat { get; internal set; }
    public static string ArgumentException_LeadingZeros { get; internal set; }
    public static object ArgumentException_MessageAt { get; internal set; }
    public static string ArgumentException_NoDigitFound { get; internal set; }
    public static string ArgumentException_StringNotClosed { get; internal set; }
    public static object ArgumentException_UnexpectedCharacter { get; internal set; }
    public static string ArgumentException_UnexpectedEscapeCharacter { get; internal set; }
    public static object[] NotSupported_UnexpectedParserType { get; internal set; }

    internal static string Format( params object[]  para)
    {
        throw new NotImplementedException();
    }
}

これで、ビルドは通るようになりました。

呼び出し側は、

class Program
{
    static void Main(string[] args)
    {
        var json = @"{ ""name"": ""JSON"", ""number"": 13 }";
        var jr = new JavaScriptReader(new StringReader(json), false);
        var o = jr.Read();
        var pairs = o as KeyValuePair<string, object>[];

        Console.WriteLine("hello " + pairs[0].Value);
        Console.WriteLine("number is " + pairs[1].Value);
    }
}

な感じで、JavaScriptReader クラスを new して、Read メソッドを使います。元の System.Json のテストプロジェクトには JsonValue のテストコードしかないのですが、まあ、これでいけます。ただし、JavaScriptReader は internal なクラスになっているので、「public class JavaScriptReader」に書き替えてしまいます。

Read メソッドの戻り値は object 型なので、適当な型に直します。これ、最終的にどうするつもりかわからないのですが、ひとまず、KeyValuePairの配列で来ているので「KeyValuePair<string, object>[]」でキャストします。

実行すると、こんな感じで、アリスが JSON に出会えます。

どうも、プロパティ名はダブルクォートで囲まないと駄目なので、RFCに沿い過ぎているというか…「{ name: “JSON” }」な JavaScript の連想配列な JSON も通してほしいですね。そういうときは、NuGet で Newtonsoft.Json をインストールせよ、という話なんですが :)  コード的にはReadNumericLiteral でプロパティ名をクォートで拾っているので、それ修正すれば ok.

サンプルコード

AliceJson-v0.1-src.zip
https://1drv.ms/u/s!AmXmBbuizQkXgfxWteUSWlBU7zrT1g

カテゴリー: C# | アリスは System.Json に出会う はコメントを受け付けていません

アリスはプラダに猫がいることを知らない

な感じで、内部データを遅延して持って来る例を作ってみる。

class Program
{
    public static List<Cat> cats = new List<Cat>();
    static void Main(string[] args)
    {
        cats.Add(new Cat(&quot;tora&quot;));
        cats.Add(new Cat(&quot;mike&quot;));

        var pradaRed = new Prada();
        var pradaBlue = new Prada();
        var pradaYellow = new Prada();

        Console.WriteLine(&quot;1: {0}&quot;, pradaRed.Cat?.Say());
        Console.WriteLine(&quot;2: {0}&quot;, pradaRed.Cat?.Say());
        Console.WriteLine(&quot;3: {0}&quot;, pradaBlue.Cat?.Say());
        Console.WriteLine(&quot;4: {0}&quot;, pradaYellow.Cat?.Say());

        return;
    }
}

class Cat
{
    string _name = &quot;&quot;;
    public Cat(string name)
    {
        _name = name;
    }
    public string Say()
    {
        return $&quot;{_name}: nya-&quot;;
    }
}
class Prada
{
    Cat _cat = null;
    public Cat Cat
    {
        get
        {
            if (_cat == null)
            {
                _cat = Program.cats.FirstOrDefault();
                if (_cat != null)
                {
                    Program.cats.Remove(_cat);
                }
            }
            return _cat;
        }
    }
}

実行するとこんな感じ

意味合いとしては、アリスはプラダのバックを3つ持っている(pradaRed、pradaBlue、pradaYellow)んだが、中に猫がはいってるかどうかは分からない。どの順番で開ける猫がいるかわからないし、猫がいるかどうかも知らない。でも、開けると猫が入っているか、また猫が入ってないかわかる。というシュレディンガーなプラダな猫の話。

cats は最初から用意されているので、外部データとして存在します。メインプログラムのアリスから見れば、最初は、Prada のオブジェクトを作るだけなので、内部の _cat が存在するかどうかは知らない。というか、知らなくてもいい状態。Cat プロパティを呼んだときにはじめて、中の cat を取り出すことができる。鞄クラスの Prada から見える Cat プロパティは、外部からみれば単純にデータを返しているように見えるが、実際は別に用意されている cats から取り出している。
つまり、MVVM パターンでいえば、View が VM に対してプロパティアクセスでデータを取り出しているときに、VM は Model からデータを引っこ抜くわけだが、そこは View が VM に問い合わせたときまで遅延される方式になっている。MVVM の大抵の例は、Model が省略されていて、View から VM のアクセスしかないのだが、ここで VM と Model との違いが明確になるよという話だ。

こんな感じで MVVM パターンを使える。Model は常に VM から参照される「制限」を付けることで、MVVM パターンの役割が担えるということだ。

じゃあ、Model(データベースや計測器など)からデータが上がってくるようなときはどうするのか?ってのは別パターンを使うことになる。この話は別の記事に書くことにしよう。

カテゴリー: 設計 | アリスはプラダに猫がいることを知らない はコメントを受け付けていません

Xamarin.Forms ビルドメモ

以前より懸案だった、OSS になった Xamarin.Forms を弄ろうと思ってコードを DL してざっと目を通しているところ。

xamarin/Xamarin.Forms: Xamarin.Forms official home
https://github.com/xamarin/xamarin.forms

image

たくさんのプロジェクトがあるが、肝なところは、

  • Xamarin.Forms.Platform
  • Xamarin.Forms.Core
  • Xamarin.Forms.Pages
  • Xamarin.Forms.Xaml
  • Xamarin.Forms.Build.Tasks
  • Xamarin.Forms.Xaml.Xamlc
  • Xamarin.Forms.Xaml.Xamlg

なところですかね。

Xamarin.Forms をビルドする

Makefile があるが、Windows 上では動きません。Linux 上でビルドします。

  1. Ubuntu に github からダウンロードして
  2. Install Mono on Linux | Mono を参照にインストール
    1. mono-devel
    2. mono-complete
    3. referenceassemblies-pcl … これがないと、/usr/lib/mono/xbuild-frameworks/ が空かも。
  3. あとは、make すれば ok. *.dll は 各プロジェクトの bin/Release 等の下にできるので、あとでかき集める?
$ ls -la /usr/lib/mono/xbuild-frameworks/
total 16
drwxr-xr-x  4 root root 4096 Sep 13 01:19 .
drwxr-xr-x 16 root root 4096 Aug 29 02:26 ..
drwxr-xr-x 11 root root 4096 Aug 29 02:20 .NETFramework
drwxr-xr-x  5 root root 4096 Sep 13 01:20 .NETPortable

ビルド時に PCL を参照するのですが、先頭が . になっているので ls –a を付けないと確認できないという罠があります。

あとは、出来上がった *.dll を Xamarin.Forms プロジェクトのものと差し替えてやればいいんじゃないかな。たぶん、ローカルの nuget フォルダーを作って、そこから適当なバージョンでダウンロードできるようにするとよいでしょう。

Xamarin.Forms.Xaml.Xamlg が *.g.cs を作成する

いくつか見ていくと、結構面白いものがみつかります。XAML を使うと、コードビハインドの対象として、*.g.cs ファイルが作られます。XAML で書かれていた x:Class とか x:Name とかは、このコードに変換されるんですよね。でもって、これが C# なゆえに、F# の XAML が使えなくてタイププロバイダーで XAML を作ったりするんですが、F# のビルド時の DLL と Xamarin で実行するときの DLL が異なるからうまくいかない、という変な状態に陥っている訳ですが(この変な状態は、以前調査したときよりも分かるようになったので、別の機会に解説を)、ならば、*.g.fs を出力するようにすればよいわけです。そうすると、Xamarin.Forms の XAML に対してコードビハインドの F# コードを作るようにできるので、XAML を扱う F# が書けますよね。まあ、現状の F# の場合は PCL の問題があって構成がややこしくなるので、.NET Core 版の Xamarin が出て来たときにもうちょっと突っ込んだほうがいい気もしますが。

ちなみに、以前つくていた、moonmile/XFormsPreviewer は、動的に XAML をロードして、XAML のタグから直接 x:Name をバインドする方法なので、タイププロバイダーともコードビハインドの *.g.cs 方式とも違います。このあたりは、状況にあわせていろいろな方法があったほういいので。

でもって、もう少し、Xamarin.Forms.Xaml.Xamlg を眺めてみて、F# コードにコンバートできるか試してみますか。というところ。

カテゴリー: Xamarin | Xamarin.Forms ビルドメモ はコメントを受け付けていません

dynamic を利用して ExDoc を書き直してみる(1)

「メタプログラミング .NET」をざっと読み終わったので、手元の github から ExDoc をダウンロードしてきて、dynamic 版の ExDoc を作っているところです。

旧 ExDoc は、演算子のオーバーライトと暗黙のキャストを使って、/,*,%演算子を使って XML を探索できていました。まあ、奇妙といえば奇妙だけど、LINQ の派生みたいな感じで作れるわけです。タグを指定するのが文字列なのがあれなのと、だったら XPath で指定してもいいんじゃないか、という話もありますが、まあ実験的に。ちなみに、このライブラリは実運用で4年ほど使っています。CakePHP をサーバーにして、クライアント側の XML パースを ExDoc を使ってパースしているという技ですね。

public void TestQuery()
{
    var xml = "<root>" + 
        "<person id='1'>masuda</person>" +  
        "<person id='2'>tomoaki</person>" +  
        "<person id='3'>yumi</person>" +  
        "</root>";

    var doc = ExDocument.LoadXml(xml);

    // query element
    ExElement el = doc * "person" % "id" == "2";
    Assert.AreEqual("tomoaki", el.Value);
    // query elements
    ExElements els = doc * "person";
    Assert.AreEqual(3, els.Count);
    Assert.AreEqual("masuda", els[0].Value);
    Assert.AreEqual("tomoaki", els[1].Value);
    Assert.AreEqual("yumi", els[2].Value);
}

で、常々、PHP の SimepleXML がうらやましかった訳ですが、そんな風に直接プロパティで設定できるのが、dynamic の良いところです。まあ、インテリセンスが効かなくなってしまうのですが、どうせ XML タグ名なのだから、そこは割り切りということで。dynamic で受けて、指定の型へキャストし直して使う方法もあるので、それもおいおいと実装する予定。「メタプログラミング .NET」にも書いてあるけど、複数のインターフェースを使って、片方のインターフェースを隠す技が使えます。確か、Xamarin.Forms の XAML ツリーのところで使っています。

dynamic 版の ExDoc を使うと、こんな風に XML タグをプロパティのように扱えます。属性に連想配列を使っているのは、SimpleXML の真似でもあるのと、doc.person.@id のようにはできなかったからなのです。先頭の @ は有効になるんだけど、TryGetMember メンバでだと、「id」としか取れないので、「@id」と「id」は同じ名前として扱われるためです。残念。

public void TestQuery()
{
    var xml = @"
<root>
<person id='1'>masuda</person>
<person id='2'>tomoaki</person>
<person id='3'>yumi</person>
</root>
";
    var doc = ExDocument.LoadXml(xml);
    // query element
    var el = doc.person["id"] == "2";
    Assert.AreEqual("tomoaki", el.Value );
    // query elements
    var els = doc.person;
    Assert.AreEqual(3, els.Count);
    Assert.AreEqual("masuda", els[0].Value);
    Assert.AreEqual("tomoaki", els[1].Value);
    Assert.AreEqual("yumi", els[2].Value);
}

まあ、それでも、連想配列は属性で、配列は子要素で示せるわけで、なかなかいい感じでパースができます。

子孫要素も含めて調べるときは「*」演算子のかわりに、タグ名に「_」を付けます。プロパティ名やメソッド名を後付けでできる(実行時に内部で分解して分岐させる)のが dynamic の良いところです。

public void TestSelectAttr()
{
    var xml = @"
<html>
<body>
<a id='a1' href='link001.html'>title</a>
<a id='a2' href='link002.html'>title</a>
</body>
</html>
";

    var st = new StringReader(xml);
    var doc = XDocument.Load(st);
    var edoc = ExDocument.Load(doc);

    var el = edoc._a["id"] == "a2";
    Assert.AreEqual("title", el.Value);
    Assert.AreEqual("link002.html", el["href"]);
}

これらをどう実装しているかというと、参照だけならば以下で ok です。

– System.Dynamic.DynamicObject を継承する
– TryGetMember を実装する
– TryGetIndex を実装する
– == 演算子を再定義する。

まあ、先行きは値の設定までやるので、TrySetMember や TrySetIndex も実装していきます。
具体的な説明は、.net core の DynamicObject のコードを読むか(コードに詳しい説明が書いてあります)、「メタプログラミング .NET」にざっと書いてあります。

public class ExElement : System.Dynamic.DynamicObject
{
    public string TagName { get { return _el.Name.LocalName; } }
    public string Name {  get { return TagName; } }
    public string Value { get { return _el.Value; } }
    internal XElement _el = null;

    public ExElement( XElement el = null )
    {
        _el = el;
    }
    public override bool TryGetMember(GetMemberBinder binder, out object result)
    {
        var els = new ExElements();

        if (binder.Name[0] == '_')
        {
            string name = binder.Name.Substring(1);
            els = SelectNodes(_el, els, name, true);
        } else
        {
            els = SelectNodes(_el, els, binder.Name);
        }
        result = els;
        return true;
    }
    private ExElements SelectNodes( XElement x, ExElements els, string name, bool deep = false )
    {
        foreach (var el in x.Elements())
        {
            if (name == el.Name)
            {
                els.Add(new ExElement(el));
            }
            if (deep)
                els = SelectNodes(el, els, name, true);
        }
        return els;
    }

    public override bool TryGetIndex(GetIndexBinder binder, object[] indexes, out object result)
    {
        result = ExDocument.EmptyElement;
        if (indexes.Count() == 1)
        {
            if (indexes[0] is int)
            {
                int i = (int)indexes[0];
                if (i < _el.Parent.Elements().Count())
                {
                    result = new ExElement(_el.Parent.Elements().ToList()&#91;i&#93;);
                }
            }
            else if (indexes&#91;0&#93; is string)
            {
                string s = indexes&#91;0&#93; as string;
                var attr = _el.Attribute(s);
                if (attr != null)
                {
                    result = new ExAttr(attr);
                }
            }
        }
        return true;
    }

    /// <summary>
    /// 探索
    /// </summary>
    /// <param name="lst"></param>
    /// <param name="value"></param>
    /// <returns></returns>
    public static ExElements operator ==(ExElement el, string value)
    {
        var result = new ExElements();
        foreach (var it in el._el.Elements())
        {
            if (it.Value == value)
            {
                result.Add(new ExElement(it));
            }
        }
        return result;
    }
    public static ExElements operator !=(ExElement el, string value)
    {
        var result = new ExElements();
        foreach (var it in el._el.Elements())
        {
            if (it.Value != value)
            {
                result.Add(new ExElement( it));
            }
        }
        return result;
    }
    public override bool Equals(object obj)
    {
        return base.Equals(obj);
    }
    public override int GetHashCode()
    {
        return base.GetHashCode();
    }

    /// <summary>
    /// 文字列にキャスト
    /// </summary>
    /// <param name="el"></param>
    public static implicit operator string(ExElement el)
    {
        return el.Value;
    }
    /// <summary>
    /// 数値にキャスト
    /// </summary>
    /// <param name="el"></param>
    public static implicit operator int(ExElement el)
    {
        return int.Parse(el.Value);
    }
    /// <summary>
    /// 実数にキャスト
    /// </summary>
    /// <param name="el"></param>
    public static implicit operator double(ExElement el)
    {
        return double.Parse(el.Value);
    }
}

もうちょっと整理して、前バージョンの ExDoc と同じように使えたら NuGet にアップしましょう。

カテゴリー: C#, EXDoc | dynamic を利用して ExDoc を書き直してみる(1) はコメントを受け付けていません

ぐるっと巡って、再び DLR へ

一時期、ブームだからと思って手に取った(リフレクションやT4目当てで)「メタプログラミング .NET」なんですが、本棚を整理するときにうっかり捨ててしまったらしく、Kindle で買いなおしました → Amazon.co.jp: メタプログラミング.NET 。どうせだから、洋書で買おうかと思ったけど、英語版の Kindle は無くて、翻訳版のほうが安いってのはどうなの?

後付けでメソッドを付けるというのを考えて、最近 Python でそれができるのを知って、そういえば IronPython ってどうなったの?と思って、再び dynamic と System.Dynamic.ExpandoObject を見直し見たら、ああ以前に ExDoc の expando 版を作ろうと思っていた残骸が HDD に残っていたのを再発見、ってのが現在位置です。でもって、「メタプログラミング .NET」の第8章の最後をよんで、やっぱり DLR って死んでたのね、と想った次第。

Iron系のプログラミングは、現在どうなっているのかわかりませんが(書籍で言及しているのが WinRT 直前だったりするので)、現在 dynamic が使われいるところとすれば、

  • ASP.NET MVC の ViewBag
  • Office COM との相互運用(主に Excel やね)

ってところでしょう。Office 組み込みの .NET は完全に死んだ感じがするし、JavaScript プラグインは生きているのか死んでいるのかも分かりません。Excel VBA は未だに健在なわけですが、VBA 自体(あるいは VB6.0)の完全互換な後継を作らない限り、Office 系の VBA はそののままで、Office 386 あたりのブラウザ版が完全に「代替」になる(デスクトップ版のExcelを駆逐するという意味で)になるかは、怪しいですよね。

特に、C# や F# に別の言語体系を組み込みたいわけではないので、DLR を詳細にやろうとは思わないのですが(Unix的には、別コマンドで呼び出せばいいわけだし)、基本的な C# の使い方とはちょっと違う面を、C# の世界に取り込みたいという願望はあります。ExDoc の演算子のオーバーロードと暗黙のキャストを多用した方式や、 D4Reflection でプライベートメソッドをリフレクションを使って呼び出してみたり、XFormsPreviewer で動的に XAML を読み込んで動的にイベントメソッドに結び付けたり、という感じで、少しメタちっくに動かしています(まあ、それぞれ頓挫しているわけですが)。

メタプログラミング .NET 当時は、System.Dynamic.ExpandoObject の詳細は見れなくて(M$ と契約している場合は別だろうけど)あれこれと、互換コードを作るしかなかったのですが、.NET Core としてオープンソース化されたので、dynamic も ExpandoObject のコードも github 上のcorefx/ExpandoObject.cs at master · dotnet/corefx で確認ができます。また、Xamarin.Forms で XAML をローディングしているところも、Xamarin.Forms 自体がオープンソースになったので、Xamarin.Forms/XamlLoader.cs at master · xamarin/Xamarin.Forms でコードを確認できます。いまのところ、.NET Core に WPF や UWP の XAML は含まれていないのですが、おおむね Xamarin.Forms.XAML と似たコードになっていると推測ができます…つーか、ここの XAML をアセンブリ内のクラスに動的にバインディングしている部分を切り離せば、各種の XAML を Parser と Loading/Binding に分けられたのに、なんでこう一括してしまうかなー、という具合なのですが。まあ、このあたりのコードをまるっと持ってきて、整合性をあわせれば、オレオレ ExpandoObject や XAML 動的ローダーも作れるというわけです。

そんな訳で、System.Dynamic.* あたりを使うと Python と同じように後付けのメソッドやプロパティを作れることが分かりました。ああ、作れるな、ってのが分かって、

class ExDoc : IDynamicMetaObjectProvider,
    IDictionary<string, Object>, 
    ICollection<KeyValuePair<string, Object>>,
    IEnumerable<KeyValuePair<string, Object>>, 
    IEnumerable, 
    INotifyPropertyChanged

な感じで用意はしていたんですよね。 でも、あっという間にくじけてしまったらしい(4年前のコードだったよ)。このあたりで ExDoc を実装していくと、XML のパーサーが、

 
var name = doc.Person.Name; 

のような感じで、プロパティのように XML のタグ名を使うことができます。これは「メタプログラミング .NET」の中にも例があります。

 
// 属性値を取る 
int age = doc.Person[0].Name.@age; 
// 要素の値を比較して、タグを検索 
var person = doc.Person.Name == "masuda"; 

こんな風な裏技ができそうな気がします。dynamic は型推論をする F# と相性が悪いので、ちょっと頓挫していたのですが、RPi + Python の組み合わせでやり直そうと思っているので、もう少し突っ込んで行こうかなと思っています。

そうそう、後で、F# と PCL の闇を書かないと。どうやら .NET Core で PCL の闇が晴れそうな気がするのですが、久し振りに PCL プロジェクトを作ろうとして、こんな風になって、闇が続いていることが判明(苦笑)

image

まあ、.NET Core に統一すればいいだよね、おそらく。

カテゴリー: C# | ぐるっと巡って、再び DLR へ はコメントを受け付けていません

メソッドの後付けを python で考える

以前に、

後付け言語 post を考えてみる | Moonmile Solutions Blog
http://www.moonmile.net/blog/archives/5866

というのを考えていたのだけど、実は pytohn でそれができることをつい最近知った。

和訳 なぜPythonのメソッド引数に明示的にselfと書くのか | TRIVIAL TECHNOLOGIES 4 @ats のイクメン日記
http://coreblog.org/ats/translation-of-why-explicit-self-has-to-stay/

Raspberry Pi を触っていると Python コードに触れる機会が多いはずなのだが、Windows IoT Core を乗せたり、mono + F# という組み合わせでやっているうちに遠ざかってしまった(苦笑)ので、全然書いたことがなかった。
機械学習がやりたいわけではないけど(画像の特定ぐらいはやらないと駄目なんだが)、

Raspberry Pi Cookbook for Python Programmers [Kindle edition] by Tim Cox
https://www.amazon.co.jp/gp/product/B00JQEJE12/ref=oh_aui_d_detailpage_o01_?ie=UTF8&psc=1

という、ちょうど RPi と Python が学べそうな本があるので、買ってみた。これ、早くに駆っておくべきでしたよ。これだったら Raspbian で電子工作ができそうです。自分が結構躓いたところが丁寧に解説してありました。
RPi のほうは、手元にある Orange Pi も含めてもう少し突っ込んでやるために、Python を使えるようになろうかなと考えているところですが、その前にあの「self」って何なんだろう、に対する答えの記事にちょうど当たりました。初回にあたったのは自分的に結構ラッキーだったかなと思って、書き残しておきます。

後付けでメソッドを拡張する

ちなみに、ごく最近学び始めたばっかりなので、python2の事情は知りません。pytohn3だけを対象にしていきます。あちこちにサンプルコードがあるのですが、それが2なのか3なのかよくわからんけど、まあ、目的は PPi や組み込み Linux でロボット制御あたりに使うことなので、どっちでもいいのです。
たぶん、似たようなことは javascript の prototype でも作れるだろうけど、演算子のオーバーロードとかラムダ式の書き方とかが、自分にしっくりしてきる(というか F# に似ている)ので、python のほうが相性がよさそうです。

# 何もないAクラス
class A:
    pass
# calc メソッドを追加する
def calc(self, x):
    return x*x
A.calc = calc

a = A()
print("ans. " + str(a.calc(10)) )

# 後付けで上書きする
def calc2(self, x):
    return x*x*x
A.calc = calc2

a = A()
print("ans. " + str(a.calc(10)) )

何もメソッドやプロパティを持たない A クラスがあって、後付けでメソッドを拡張します。いわゆる拡張メソッドなのですが、最初の「def calc(self, x):」なところは、普通にグローバルな関数宣言ですよね。グローバルなクラスのメソッドのstaticメソッドとも言えます。F# なら、

let calc self x = 
    x*x

とするところです。このグローバル関数の self 引数は余分なのですが、Python の場合、後付けで A.calc メソッドを付けるときに効いてきます。

def calc(self, x):
    return x*x
A.calc = calc

C++ ならば、A クラスの calc 関数ポインタにグローバル関数を割り当てる、というイメージですよね。C++ の場合、型チェックがきついので、実際は A:calc ポインタになってしまい、グローバル関数の calc を割り当てることはできません。std::function テンプレートを使って、うまいことをやります。
C# や F# の場合も関数ポインタにして用意することもできるのですが、固定であるならば拡張メソッドを使ったほうが楽です。

type A() =
    do ()
let calc self x = 
    x*x
type A with
    member x.Calc n = calc x n 

空の A クラスを作っておいて、グローバルな calc メソッドを、A.calc メソッドから呼び出します。引数の self が無駄になっているように見えますが、実は、member x.Calc の x な部分に対応しています。つまり、F# であっても、ちょうど self/this な部分を参照できるような仕組みになるわけです。

似たようなコードを C# で書くともっと明確になります。

class A {}

class Global {
    static int Calc( A a, int x ) {
        return x*x;
    }
}

class AEx {
    static int Calc( this A a, int x ) {
        return Global.Calc( a, x )
    }
}

C# の場合、グローバルな関数を置けないので、Global クラスを作って static メソッドにしておきます。このとき、無駄な A クラスへの参照を置くわけですが、拡張メソッドを AEx.Calc として作るときに、this として A クラスのオブジェクトを置きます。つまり、明示的に this が使われていることが分かります。

こうなると、Python のメソッドは、実は C# で言うところの「拡張メソッド」にあたるのではないか?という類推ができます。

class B:
    def calc(self, x):
        return x*x

Python のクラスのメンバ関数にいちいち置かれている self は、実はグローバル関数の self 引数なのかもしれません。
そう思うと、グローバル関数と、クラスのインスタンスメソッド、クラスに後付けされるメソッドの3つの関数が同じ型を持っていることに納得がいきます。なるほど、あいまいな型のままにメソッドが呼び出されているわけではなくて、むしろ型チェックのために「self」が置かれている、と考えることができます。いや、実動作はわからないのだけど、論理的にはそう考えられるという訳です。

それらの関数が、常に関数ポインタとして働き、入れ替え可能であるという点で、もういちど、A.calc に別のメソッドを割り当てることも可能になります。

def calc2(self, x):
    return x*x*x
A.calc = calc2

実行してみると、クラスに割り当てたインスタンスメソッドがきちんと動作します。

class A:
    pass

# calc メソッドを追加する
def calc(self, x):
    return x*x
A.calc = calc

a = A()
print("ans. " + str(a.calc(10)) )

# 後付けで上書きする
def calc2(self, x):
    return x*x*x
A.calc = calc2

a2 = A()
print("ans. " + str(a2.calc(10)) )
print("ans. " + str(a.calc(10)) )

この順番で実行してみると、

ans. 100
ans. 1000
ans. 1000

のように、最初の a.calc の値と2回目の a.calc の値が異なります。同じオブジェクト(インスタンス)であっても、違う結果が出るところが面白いですよね。バグの温床になりそうですが(苦笑)。

もちろん、javascript の prototype プログラムのように、オブジェクトを対象にしてメソッドを追加することもできます。

a.calc = lambda x: x*x*x*x
print("a  ans. " + str(a.calc(10)) )
print("a2 ans. " + str(a2.calc(10)) )

こんな風にラムダ式で a オブジェクトのほうにだけ calc メソッドを上書きしてやると、a オブジェクトのほうだけ計算結果が変わります。

a  ans. 10000
a2 ans. 1000

実際は、こんな風に無造作に後付けで拡張してしまうとえらいことになってしまいそうですが、オブジェクト内の変数を弄ってクラスの振る舞いを変えるのではなく、メソッドそのものを切り替えてしまうのは面白い発想です。ダックタイピングを、クラスの利用者側から強制できるし、メソッド名から類する動作をユーザー側から決められるということでもありますね。先の例で言えば、利用するときに A クラスにあらかじめ calc メソッドがあればそれを使うし、無ければ利用者側が追加できるというイメージです。また、提供側の calc が気に入らなkれば、利用者側から書き替えてしまう。その変更は、提供側の動作にも影響を与えるわけですが、calc メソッドとしての動作が理屈にあっていれば(契約プログラミング的に)入れ替えることも自由なわけです。
利用側から内部に影響を与えるという話は、ライブラリが堅牢であることの根拠を崩すことになる(ライブラリは手をつけられない、privateな空間であるということ。または、一定の委譲ルールでしか触れないということ)のですが、実際は、利用者がライブラリを使う以前にライブラリが存在していなければならず、ライブラリの制作者未来を見通すことができない(完全にはできないので、処方箋を示すことしかできない)ので、利用者が使うときには「未知」にならざるを得ない。ならば、一定のルールでもって、ライブラリ自体に介入することを、利用者側から許してもいいのではないか?というのが「後置型」の発想のもとで、先の python のメソッドの後付けが利用できるなと思ったところです。アスペクト指向やDIよりも、もっとライブラリ内部に踏み込んでよい、という発想です。
このあたりは、もうちょっと python を弄って試してみたいところですね。

カテゴリー: 開発, F#, python | メソッドの後付けを python で考える はコメントを受け付けていません

アリスはアノテーションがお嫌い

アリスはリフレクションがお嫌い | Moonmile Solutions Blog
http://www.moonmile.net/blog/archives/8092

の続きで、属性/アノテーションの話を追記。

リフレクションはクラスに対して static な情報だということが解ったら、じゃあ「属性」ってのも static な情報というのが分かるので、このあたりは同根なんだなというのが分かる。ケント・ベック氏の「テスト駆動開発入門」に、とあるプログラム言語を学ぼうとしたときに自動テストツール(xUnit)を書いてみると、パターンが網羅されていて学びやすい、ということが書いてある。実際、xUnit のツールにはデザインパターンが多く使われていて、その実現をそれぞれのプログラム言語がどうやって実装しているのかというのを見ることができる。
よくあるテストメソッドとテストクラスの情報収集の方法に

– C++ の場合は、あらかじめ登録が必要
– Java の場合は、先頭にTestがくっついている
– C# の場合は [TestMethod] 属性を付ける

という違いがある。今だともうちょっと違っているけど、初期のころの xUnit のコードを見ると、その当時の文法で駆使されていて、その後にちょっとずつ他の言語の流儀が取り入れられているのが分かります。
C++ の場合は、リフレクションがないので、テストメソッドをいちいち登録するしかない…けど、テンプレートを駆使して明示的には登録しているようにみえないけど、実は登録してるって方法を取ってる。

TEST_CLASS(UnitTest1)
{
public:
	TEST_METHOD(TestMethod1)
	{
		auto m = new Prada();
		m->setStyle(10);
		m->setColor(&quot;Pink&quot;);
		auto plist = m->properties;
		for each ( auto prop in plist )
		{
			auto n = prop.first;
			auto v = prop.second;
			void*x = v(m);
		}
	}
};

最近の VC++ でテストプロジェクトを作ると、TEST_CLASSとTEST_METHODというマクロが定義されていて、うまく登録できるようになっている。

初期の Java の場合は、属性/アノテーションがなかったので、実行したいテストメソッドと普通のメソッドを区別するために、メソッドの先頭に「Test」とつけておく。でもって、リフレクションを使って先頭が Test になっているメソッドだけを実行するという方式をとる。
これは、結構シンプルでよいのだけど、メソッドの名前自体に意味を持たせてしまうのが、なんだかなーという感じだったので、最近の JUnit では、「@Test」というアノテーションが付けられる。

public class JunitExampleTest {

    @Test
    public void testFoo() {
		JunitExample example = new JunitExample();
        assertEquals(1, example.foo())
    }
}

Java の後発になる C# の場合は、リフレクションだけでは足りないとみて、属性(Attribute)というのものを付けている。実際の使い方はアノテーションと同じだ。

[TestClass]
public class UnitTest1
{
    [TestMethod]
    public void TestMethod1()
    {
        var m = new Prada { Style = 10, Color = "Pink" };
        var ti = m.GetType().GetTypeInfo();
        var plist = ti.DeclaredProperties;
        foreach (var prop in plist)
        {
            var n = prop.Name;
            var v = prop.GetValue(m);
            Debug.WriteLine($"prop: {n} value: {v}");
        }
    }
}

当初、属性というものが使われていたときに、他の言語にはアノテーション自体の機能がなかったのと、アスペクト指向が流行り始めていて、そのアノテーション(装飾)と区別がつかなかったという経緯もあって、なんかいまいちな言語仕様な感じがしたのだが、MVCパターンのように動的に Action メソッドを呼び出すようなスタイルを考えたとき、ちょっと便利に使える。

[Produces(&quot;application/json&quot;)]
[Route(&quot;api/People&quot;)]
public class PeopleApiController : Controller
{
    private readonly ApplicationDbContext _context;

    public PeopleApiController(ApplicationDbContext context)
    {
        _context = context;
    }
    // GET: api/People
    [HttpGet]
    public IEnumerable<Person> GetPerson()
    {
        return _context.Person;
    }
    // GET: api/People
    [HttpGet]
    [Route(&quot;search/name/{value}&quot;)]
    public IEnumerable<Person> GetPersonByName([FromRoute] string value )
    {
        return _context.Person.Where( t => t.Name.Contains(value));
    }
    // GET: api/People
    [HttpGet]
    [Route(&quot;search/age/{value}&quot;)]
    public IEnumerable<Person> GetPersonByAge([FromRoute] int value)
    {
        return _context.Person.Where(t => t.Age >= value);
    }

これは、ASP.NET MVCアプリケーションのルーティングのテスト用に作ったサンプルコードだが、たくさんの属性が使われている。要は、クラスに対して static な情報を属性に詰め込めばよいので、Produces属性でJSON形式を返すとか、Route属性でURL呼び出しの形式を設定するとか、HttpGet属性でGETメソッドだけに呼び出しを限るとかの設定ができる。
これらの情報はDI(インジェクション)という形で、設定ファイルなどに追い出すことも可能なのだが、そもそも設定ファイルとこのControllerクラスが1対1になっているのだったら、その「設定」自体をコードで書こうと外部の設定ファイルに書こうと意味は同じはずだ。ならば、属性で、ということになる。当然、コードに埋め込まれてしまう欠点もあって、ちょっとだけルーティングを変えようと思っても、このコードを変えないといけないので、そういう場合は DI のように設定自体を外付けしてしまったほうがよい。
これは、属性とDIのどちらがいいという話ではなくて、利用するときの状況によりけり、というパターンかなと思っている。一般的な使い分けとしては、コーディング時に決定してしまうもの(あとからの設定では変えられないもの、代替できないもの)は属性に、動作環境によって変わるもの(データベースの接続文字列とか)は設定ファイルに出せばよい。

属性を使わないで実装する

じゃあ、本題として、ルイスはアリスが属性/アノテーションが嫌いだから、属性を使わないで自前で実装するんやという話になっているので、ASP.NET MVC っぽい Action メソッドの呼び出しをどうやって実装するのかを考えてみる。初期の JUnit のように諸々の規約をメソッド名に追加してもよいのだが、あれこれ設定するのは大変だ。

class AliceAttribute : Attribute { }
class DefaultAttribute : Attribute
{
    public string Name { get; set; }
}
[Alice]
class Prada
{
    public int Style { get; set; }
    [Default(Name = "White")]
    public string Color { get; set; }
}

こんな風な Alice 専用の Prada クラスを作ってみる。クラスの属性に「Alice」を付けておくと、Alice 専用のクラスという訳だ。ついていない場合は、普通のクラス。
あと、デフォルトの値を設定できるように、Default属性を作ってみる。

public void TestMethod3()
{
    var m = new Prada { Style = 10, Color = &quot;Pink&quot; };
    var ti = m.GetType().GetTypeInfo();

    var attr1 = ti.GetCustomAttribute<AliceAttribute>();
    var attr2 = ti.GetProperty(&quot;Color&quot;).GetCustomAttribute<DefaultAttribute>();

    Debug.WriteLine($&quot;prop: Color attr: {attr2.Name}&quot;);

}

C# での属性呼び出しは、GetCustomAttributeメソッドを使えばよい。GetCustomAttributeで null が返ってきたら属性が設定されていないという訳で、Alice 属性がついているかどうかが分かる。全ての属性を取りたい場合は、GetCustomAttributes メソッドを使うが、どんな属性にも対応するということはないのであまり使わないだろう。クラス特性を調べるときとかに使うかな。
Default 属性で設定した Name プロパティは、そのまま属性オブジェクトの Name プロパティで見れる。この属性オブジェクトが、クラスやメソッドに設定したときにクラス情報と一緒にくっついてくるというイメージでよい。結局のところ、GetTypeInfoメソッドを呼び出してクラス情報を取るので、リフレクションと同じ方式になる。

これを C++ で書き直してみると、

class AliceAttribute {};
class DefaultAttribute {
public: 
	std::string Name;
	DefaultAttribute(std::string &name) { this->Name = name;  }
};

class Prada2 {
private:
	int _style = 0;
	std::string _color = &quot;&quot;;
public:
	int getStyle() { return _style; }
	void setStyle(int v) { _style = v; }
	std::string getColor() { return _color; }
	void setColor(std::string v) { _color = v; }
public:
	static AliceAttribute *Alice;
	static DefaultAttribute *DefaultColor;
	Prada2() {
		Prada2::Alice = new AliceAttribute();
		Prada2::DefaultColor = new DefaultAttribute(std::string(&quot;White&quot;));
	}
};
TEST_CLASS(UnitTest2)
{
public:
	TEST_METHOD(TestMethod1)
	{
		auto m = new Prada2();
		m->setStyle(10);
		m->setColor(&quot;Pink&quot;);

		auto alice = m->Alice;
		auto color = m->DefaultColor->Name;
	}
};

適当な属性クラス(AliceAttribute、DefaultAttribute)を用意して、Prada2クラスの static メンバとして設定しておく。汎用的に作る場合は、リフレクションのときと同じように map を使うのだが、クラス自身の属性とメンバの属性があるので2つ作らないといけないのがちょっと面倒…だけでも、まあできないことはない程度にはわかるだろう。

逆にいえば、クラスの情報として、static で設定しているメンバ変数は属性に置き換えられる、ということだ。リフレクションを使うので、呼び出し側でひと手間かかるが、この設定の命名規約があれこれと悩むよりも、Attribute クラスを適当に作ってしまって属性に対する設定のほうで、あれこれと変えるということもできるということだ。
そういう意味で、

– クラスが外部公開する const 変数
– 属性
– DI の設定

を使い分けていくことになる。

カテゴリー: 開発 | アリスはアノテーションがお嫌い はコメントを受け付けていません

アリスはリフレクションがお嫌い

長年使っていた Perl+C# の WordPress 投稿ツールを、.net core を機会に F# に直そうとしている途中なのですが、そのなかでふと、リフレクションを使わずに XML-RPC を実装するのはさぞしんどいことではないか?
ということに思い至りました。いや、もともと使っていた XMLRPC.NET はインターフェース形式なのであれこれとややこしくなっているんですよね。そのあたりで使い勝手が悪くなってしまっているのと、ストレス発散ののためにも F# で書き直しをしています。

実は、MVCパターンのControllerの呼び出しにもリフレクションが使われます。ブラウザから呼び出されたURLアドレスからActionメソッドの呼び出しがそれで、Controllerクラスのメソッドをあらかじめ登録しているのではなくて、実行時にメソッドを探します。ASP.NET MVC だといちいちコンパイルをしないと駄目だけど、CakePHPのようなパターンだと動的言語なのでスクリプトを直接修正することができます。

そんなわけで、XML-RPC だと

class Prada
{
    public int Style { get; set; }
    public string Color { get; set; }
}

なクラスを

<struct>
  <member>
    <name>Style</name>
    <value>
      <int>10</int>
    </value>
  </member>
  <member>
    <name>Color</name>
    <value>
      <string>Pink</string>
    </value>
  </member>
</struct>

な風にXML形式に直します。実際はmethodCall要素があったり、今だったらJSON形式のほうが楽だろうってことなんですが、WordpressにXMLRPCで投稿すると楽なので自作ツールを作って使っています。

リフレクションを使う

じゃあ C# でXML形式に直すことを考えると、PradaクラスのStyleプロパティとColorプロパティをどうにかして持ってこないといけないので、プロパティを列挙するリフレクションを使えばいいんだね、ということになります。

using System.Reflection;

public void TestMethod1()
{
    var m = new Prada { Style = 10, Color = "Pink" };
    var ti = m.GetType().GetTypeInfo();
    var plist = ti.DeclaredProperties;
    foreach (var prop in plist)
    {
        var n = prop.Name;
        var v = prop.GetValue(m);
        Debug.WriteLine($"prop: {n} value: {v}");
    }
}

こんな風に、GetTypeInfoメソッドでクラス情報を取ってきて、DeclaredPropertiesコレクションでプロパティの一覧を取得、その後で名前と値を取ってきます。GetValueメソッドで取ってきたものは object 型なので、実際に利用するときは元の型に戻さないといけません。型自体は、PropertyType プロパティで取って来れるので、これをそれぞれの型と is 演算子で比較するか、乱暴な場合は as でキャストしてしまいます。
XML-RPCのときは最終的に文字列に直してしまうので、ToString で代用させています。

ちなみに、F# で書くとこんな感じになります。

type Prada() =
    member val Style = 0 with get, set
    member val Color = &quot;&quot; with get, set

[<TestMethod>]
let TestMethod1() =
    let m = new Prada( Style=10, Color=&quot;Pink&quot; )
    let ti = m.GetType().GetTypeInfo()
    let plist = ti.DeclaredProperties
    for prop in plist do
        let n = prop.Name
        let v = prop.GetValue(m)
        System.Diagnostics.Debug.WriteLine(String.Format(&quot;prop: {0} value: {1}&quot;, n, v ))

この列挙したプロパティを使って XML 形式に直すわけですが、そのあたりは XElement を使うと楽にできるので、ちょっと省略。

で、アリプラの本題にいきます。
これって、リフレクションを使うと「クラス情報」を取れることになるけど、内部実装的にはどうなっているんだろうか。いや、そもそも、C++ のようなクラス情報を持たない言語の場合は、どうやって実装すればいいんだろうか?

という話になります。クラス情報なんだから、クラスに紐づいているわけで、new で作成したオブジェクトとは別ですよね。なので、いくつもオブジェクトを作ったとしても、クラス情報とかプロパティの型とかは一種類しかないわけです。Javascriptのようにプロトタイプで後からどんどん付け加えていくというパターンもあるので、プロパティやメソッドが固定というわけではないけれど(実装的には、後付け用のコレクションを用意するわけだし、dynamic型みたいな感じになる)、もともとの固定のプロパティやメソッドをどうやって扱おうかということになります。

リフレクションもインターフェースも使わないで実装する

なので、アリスは「リフレクション」も「インターフェース」も嫌いなので、ルイスは何か別の方法で実装しなければいけません。本来ならば、インターフェースを作ってという話になるけど、できるだけリフレクションっぽい形にしたい。あと、クラスの属性のように付加情報も取り入れたい。

class PradaNew
{
    public static string ClassName = &quot;PradaNew&quot;;
    public static Dictionary<string, Func<PradaNew, object>> Properties =
        new Dictionary<string, Func<PradaNew, object>>
    {
        {&quot;Style&quot;, m => m.Style  },
        {&quot;Color&quot;, m => m.Color }
    };
    public int Style { get; set; }
    public string Color { get; set; }
}

そんな訳で、ルイスは「PradaNew」クラスを作って、付加情報を独自に追加しました。なんか、それっぽい形で取れるように、クラス名と、プロパティ名を渡すと値が取れるような仕組みを作っておきます。
両方とも static プロパティなので、クラスのみに紐づいた情報になりますね。

でもって、PradaNew を使ったリフレクションっぽい形の例がこっち。

public void TestMethod2()
{
    // リフレクションを使わないパターン
    var m = new PradaNew { Style = 10, Color = "Pink" };
    var ti = m.GetType().GetTypeInfo();
    var plist = PradaNew.Properties;

    foreach (var prop in plist)
    {
        var n = prop.Key;
        var v = prop.Value(m);
        Debug.WriteLine($"prop: {n} value: {v}");
    }
}

プロパティ名を Key で、値のほうを Value で取ってくるのが多少乱暴ですが、先のリフレクションと同じようなスタイルになります。
ここにクラスの属性として「HttpGet」とか加えたいときも、ClassName プロパティのように static なプロパティを追加すれば ok ですね。static なプロパティなので、オブジェクトが大量に増えてもメモリは増えません。また、値を取ってくるところは適当にキャッシュしてやれば、通常のメソッド呼び出しと遜色ないスピードが出せそうだということがわかります。

これを、ちょっと C++ で直してみます。

#include <map>
#include <functional>

class Prada {
private:
	int _style = 0;
	std::string _color = &quot;&quot;;
public:
	int getStyle() { return _style; }
	void setStyle(int v) { _style = v; }
	std::string getColor() { return _color; }
	void setColor(std::string v) { _color = v; }

public:
	const std::string className = &quot;Prada&quot;;
	static std::map <std::string, std::function<void*(Prada*)>> properties;

	Prada() {
		auto func = [](Prada* m) { auto v = m->getStyle(); };
		Prada::properties.insert(std::make_pair(std::string(&quot;Style&quot;), [](Prada* m) { auto v = m->getStyle(); return &v; }));
		Prada::properties.insert(std::make_pair(std::string(&quot;Color&quot;), [](Prada* m) { auto v = m->getColor(); return &v; }));
	}
};
std::map <std::string, std::function<void*(Prada*)>> Prada::properties;;

int main()
{
	auto m = new Prada();
	m->setStyle(10);
	m->setColor(&quot;Pink&quot;);
	auto plist = m->properties;
	for each (auto prop in plist)
	{
		auto n = prop.first;
		auto v = prop.second;
		void* x = v(m);
		// void* から元の型へ戻すには?
	}
	return 0;
}

元の型に戻すのには、プロパティの型を調べる必要があるので、もう少し手を入れないといけないけど、だいたいはこんな感じ。map にしてラムダ式を返すようにしたのでややこしいことになっていますが、構造体を作ってプロパティの型を調べるところと、値を取得するところを作ったほうが楽かも。

まあ、こんな実装は普通はしないわけで、内部的にはこんなことになっているなーと思いつつ、リフレクションを道具として使うとよいのではと思った次第。

カテゴリー: 開発 | アリスはリフレクションがお嫌い はコメントを受け付けていません