SDD(Sleep-Driven Development)

睡眠の重要性!!睡眠の重要性!!

Xamarin.iOSにおける循環参照

この記事は[初心者さん・学生さん大歓迎!] Xamarin その1 Advent Calendar 2017の12日目の記事です。
昨日は@kmz_kappaさんでXamarinでも難読化のすゝめでした。

今回はXamarin.iOSをやる上で気をつけたほうがいいと思ったメモリリークのお話です。
基本的にはXamarin.iOS特有のお話でXamarin.FormsやXamarin.Androidは大丈夫なはず。。。
ただし、Xamarin.FormsでCustomRenderer等を用いてNSObjectを触る場合は注意が必要です。

また、もし書いてあることに誤りがあったときはご指摘をよろしくお願いします🙇

環境

循環参照とは

循環参照とは、以下のコードのようにHogeというオブジェクトとFooというオブジェクトがお互いに参照を持ってしまっている状態です。

class Hoge 
{
    Foo foo;

    public Hoge() 
    {
       foo = new Foo(this);
    }
}

class Foo 
{
    Hoge hoge;

    public Foo(Hoge hoge) 
    {
        this.hoge = hoge;
    }
}

.NET Frameworkでは特に問題ないコードですが、Xamarin.iOSでは特定条件下でオブジェクトが破棄されなくなる可能性があります。

iOSにおける循環参照問題

.NET Frameworkなどのオブジェクトを追跡して破棄するGCを採用している場合は上記コードでも問題は起きないのですが、代入などでオブジェクトの参照が増えたときにカウントを増やし、0になったら破棄をする参照カウント方式のGCを使っているiOSでは問題が発生します。

たとえば

void NewHoge() 
{
    var hoge = new Hoge();
}

としたとき、hogeの参照カウントは+1されます。 また、コンストラクタ内でFooをHogeのフィールドfooに代入してfooの参照カウントは+1されます。 そしてFooにthisを渡しているのでhogeの参照はまた+1されます。

このとき、上記例でメソッドを抜けた場合hogeは破棄されることを期待します。 が、メソッドを抜けたことでhogeの参照カウントは-1されますが、まだhogeのフィールドfooが持っている参照が残っています。 参照カウント方式では参照カウントが0にならないとオブジェクトは破棄されないので0になっていないhogeは破棄されません。 また、foohogeが参照を持っているため破棄されません。

よって永遠にhogefooの参照カウントは0にならず、NewHoge()というメソッドを抜けたあとも生き続け、メモリリークとなるのです。

Xamarin.iOSにおける循環参照問題

上の例ではすぐに循環参照になりそうですが、Monoは優秀で実は上記のようにただフィールドで循環参照した場合ではきちんとオブジェクトは破棄されます。

しかしNSObjectが関わってきた場合は注意が必要です。 たとえば、

public class Hoge : UIView
{
    public Foo Foo { get; set; }
    public Hoge() : base(CGRect.Empty)
    {
        Foo = new Foo {Hoge = this};
    }
}

public class Foo : UIView
{
    public Hoge Hoge { get; set; }
}

上記コードは先程のとおりオブジェクトが使われなくなったときに破棄されます。

しかし、

public class Hoge : UIView
{
    public Hoge() : base(CGRect.Empty)
    {
        var foo = new Foo {Hoge = this};
        AddSubview(foo);
    }
    ...
}
...

のようにUIViewのメソッドであるAddSubviewに循環参照しているfooオブジェクトを渡すとオブジェクトが破棄されなくなります

これはメソッドにかぎらず、プロパティやイベントに循環参照したオブジェクトを渡しても発生します。

特にイベントなどでラムダ式を使うと、注意していても循環参照するときがあります。

たとえば、

public class ViewController : UIViewController
{
    protected ViewController(IntPtr handle) : base(handle)
    {
        // Note: this .ctor should not contain any initialization logic.
    }

    public override void ViewDidLoad()
    {
        base.ViewDidLoad();
        // Perform any additional setup after loading the view, typically from a nib.

        var button = new UIButton();
        button.TouchUpInside += (sender, args) =>
        {
            var viewController = this.Storyboard.InstantiateInitialViewController();
            this.NavigationController.PushViewController(viewController, true);

        };

        View.AddSubview(button);
    }
}

上のようなコードは.NETではよく見そうなコードですが、ViewControllerとbuttonが循環参照してオブジェクトが破棄されません。

これはbutton.TouchUpInsideに渡しているラムダ式内でthisを使うことによってViewControllerの参照がキャプチャされbuttonオブジェクトが持ってしまい、循環参照になってしまうのです。

循環参照の解決方法

循環参照はお互いに参照を持っていることが問題なので適切なタイミングで開放するようにすれば問題ありません。

いくつか方法はあるのですが、ここでは2つ書きます。

WeakReference

1つ目の方法は、オブジェクト同士がお互いに強く参照していることが問題なので片方を弱参照にすれば大丈夫です。

public class Hoge : UIView
{
    public Foo Foo { get; set; }

    public Hoge() : base(CGRect.Empty)
    {
        var weakRef = new WeakReference<Hoge>(this);
        Foo = new Foo(weakRef);
        AddSubview(Foo);
    }
}

public class Foo : UIView
{
    WeakReference<Hoge> weakHoge;

    public Hoge Hoge
    {
        get
        {
            if (weakHoge.TryGetTarget(out var hoge))
            {
                return hoge;
            }

            return null;
        }
    }

    public Foo(WeakReference<Hoge> weakHoge) : base(CGRect.Empty)
    {
        this.weakHoge = weakHoge;
    }
}

AddSubViewをしていますが、上記コードは問題なくオブジェクトが破棄されます。

手動で開放

2つ目はオブジェクトが破棄されるタイミングでも循環参照が残っているのが問題なので、自分で開放してあげます。

public class Hoge : UIView
{
    public Foo Foo { get; set; }

    public Hoge() : base(CGRect.Empty)
    {
        Foo = new Foo(this);
        AddSubview(Foo);
    }

    protected override void Dispose(bool disposing)
    {
        if (disposing)
        {
            Foo.RemoveFromSuperview();
        }
        base.Dispose(disposing);
    }
}

public class Foo : UIView
{
    Hoge hoge;
    public Foo(Hoge hoge) : base(CGRect.Empty)
    {
        this.hoge = hoge;
    }
}

ここで注意なのが、Dispose(bool)メソッドは正しく実装されていた場合オブジェクトが破棄されるタイミングで呼び出されますが、Finalizerからの呼び出しなのでdisposingはfalseになります。 なので上記コードの場合はHogeが必要なくなったタイミングでDispose()メソッドを呼び出して上げる必要があります。

また、イベントの場合、

public class ViewController : UIViewController
{
    protected ViewController(IntPtr handle) : base(handle)
    {
        // Note: this .ctor should not contain any initialization logic.
    }

    public override void ViewDidLoad()
    {
        base.ViewDidLoad();
        // Perform any additional setup after loading the view, typically from a nib.

        var button = new UIButton();
        button.TouchUpInside += (sender, args) =>
        {
            var viewController = Storyboard.InstantiateInitialViewController();
            NavigationController.PushViewController(viewController, true);
        };

        View.AddSubview(button);
    }
}

のような書き方だとラムダ式なのでイベントの購読を解除できず、またViewDidUnloadは非推奨で使用すべきではないので解除するタイミングがありません。

なので、

public class ViewController : UIViewController
{
    UIButton button;
    
    protected ViewController(IntPtr handle) : base(handle)
    {
        // Note: this .ctor should not contain any initialization logic.
    }

    public override void ViewDidLoad()
    {
        base.ViewDidLoad();
        // Perform any additional setup after loading the view, typically from a nib.

        button = new UIButton();
        View.AddSubview(button);
    }

    public override void ViewWillAppear(bool animated)
    {
        base.ViewWillAppear(animated);
        
        button.TouchUpInside += OnTouchUpInside;
    }

    public override void ViewDidDisappear(bool animated)
    {
        base.ViewDidDisappear(animated);

        button.TouchUpInside -= OnTouchUpInside;
    }

    private void OnTouchUpInside(object sender, EventArgs args)
    {
        var viewController = Storyboard.InstantiateInitialViewController();
        NavigationController.PushViewController(viewController, true);
    }
}

のようにViewWillAppearでイベントを購読、ViewDidDisappearでイベント購読解除すれば大丈夫です。

循環参照の調べ方

Xamarin Profilerが便利そうなのですが、Enterpriseライセンスを持ってないので詳細はわかりません。

Xamarin Profiler について - Xamarin 日本語情報

一番簡単な方法は、NSObjectならDispose(bool)メソッドをoverrideしてConsole.WriteLine()なりなんなりする方法です。

循環参照をするとオブジェクトが破棄されなくなるのでFinalizerから呼び出されるDispose(bool)メソッドも呼び出されなくなります。

ただし、iOS Simulatorだと結構頻繁にDisposeメソッドが呼ばれていることが確認できるのですが、iOS実機だとなかなかDisposeメソッドが呼ばれません。

これはiOS SimulatorだとGC.Collect()?が頻繁に呼び出されているからだそうです。
memory management - the garbage collector in Xamarin Ios it not working on devices - Stack Overflow

なので実機ではどこかにGC.Collectを仕込むか、リソースを食う処理を頻繁に起こすか、ひたすら待つかして確認する必要がありそうです。

もっといい方法があれば教えてください。。

まとめ

ここではiOSで初めて開発する人が嵌りそうな循環参照によるメモリリークについて書きました。

C#の世界にいる限り、循環参照によるメモリリークは大丈夫なはずですが、UIView.AddSubviewのようなメソッドやプロパティを通してC#の世界の外に出ると注意しなければメモリリークになってしまいます。

ここいら辺かなりややこしいのでまだまだ罠がありそうですが、

  • 循環しそうなフィールドやプロパティを持つときはWeakRefenrenceを使用する
  • イベントはしっかり購読解除する

などを心がけていれば大丈夫だと思います。

明日は@DevTakasさんです。
よろしくお願いします!

Kotlin/NativeのiOSサンプルを動かしてみる

Kotlin/Nativeが頑張っているっぽいので、iOSのサンプルを実行してみました。

・・・実行しただけです。

環境

ちなみに

私はビルドし終わったあとに気づきましたが、GitHubReleaseにおそらく各種プラットフォーム向けにビルドされたやつがあるっぽいです。。

なのでそこから持ってきた場合はiOSで実行できるようにするまで飛ばせるはずです。
時間が有り余っている人はCloneからお読み下さい。

kotlin-nativeリポジトリのClone

今回はKotlin/Nativeのリポジトリに含まれているsamples/uikitを実行してみたいと思います。 まずはリポジトリをClone。

$ git clone https://github.com/JetBrains/kotlin-native.git

次にkotlin-nativeのディレクトリに移動し、依存関連をダウンロード。

$ cd kotlin-native
$ ./gradlew dependencies:update

ちょっと時間がかかるかもしれません。軽く筋トレしていましょう。

そしてコンパイラや、各種プラットフォームのライブラリをコンパイル

$ ./gradlew bundle

ただし、README.mdに

The build can take about an hour on a Macbook Pro.

と書かれている通り、ビルドに1時間近くかかります(私の環境で46分でした)。 待っている間は筋トレでもしてましょう。

ビルドが終わったらKotlin/Native側の準備は完了です。
さっそくiOSプロジェクトを見ていきましょう!

iOSで実行できるようにする

samples/uikitに移動し、.xcodeprojをオープン。

$ cd samples/uikit
$ open konan.xcodeproj

早速実行、、、といきたいところですが、いくつか修正が必要です。

まず最初にBundle Identifierを変更します。
初期状態だとorg.jetbrains.konanになっているのでここを適当に変更します。

f:id:crocus7724:20171107021121p:plain

私は今回com.example.konanにしました。

また、SigningTeamを自分のに指定します。

次にRunボタンをクリックしてみるとわかるのですが、ビルドが

cp: /~/kotlin-native/samples/uikit/build/konan/bin/app.kexe: No such file or directory

でこけます。

どうやらkotlinのコードをビルドしたとき、成果物がuikit/build/konan/binに吐き出される想定のはずなのですが、ないっぽいです。

実は成果物はuikit/build/konan/bin/iphoneに吐き出されるようなので単純にミスみたいです()

ということで、Build PhasesReplace Binaryを以下のように修正。

cp "$SRCROOT/build/konan/bin/iphone/app.kexe" "$TARGET_BUILD_DIR/$EXECUTABLE_PATH"

f:id:crocus7724:20171107021339p:plain

さあエラーも消えたし実行!!してみるとビルドは通りますが、実行されません。

実はTargetをシミュレータにしていたのですが、どうやらシミュレータ用のバイナリを吐いてくれないらしく、再生ボタンを何回か押すと以下のようなメッセージが表示されます。

f:id:crocus7724:20171107021356p:plain

なので、MacbookiPhoneを繋いで実機ビルドしましょう!

実行ボタンを押すとビルドされ、実機デプロイされるのですが、以下のメッセージが表示され、初回は時間がかかります。

f:id:crocus7724:20171107021417p:plain

筋トレしていましょう。

うまくいけば実行できているはずです!!

f:id:crocus7724:20171107021511j:plain

お疲れ様でした!!

まとめ

仕組みはどうなっているのかなーと思ったのですが、.xcodeprojのBuild Phasesを見た感じ、

  • kotlinコードをkonanc?を使ってネイティブコンパイル
  • 成果物を$EXECUTABLE_PATH(konan.app/konan)にReplace

しているっぽいですね。

なのでobjcコードは完全にダミーでIBホニャララするくらいしか使わない感じ?

あと、現状唯一Kotlin/Nativeに対応しているIDEがCLionなのですが、開いてみた感じまだ対応されていないので補完なしで頑張るしかないっぽいですね(非常に辛い)。

また、Common moduleがない?(みつからなかった)のでネイティブAPIで頑張るしかないみたいです(非常に辛い)。

現状iOS開発するにはとてもとてもとても辛いですが、将来Common moduleが出たりIDE対応が進んだりすればきっと選択肢の中に一瞬入るようになると期待しています。

Visual Studio for Mac Extensionで独自Padを作る

Visual Studio for Macでの独自Padの実装方法です。 ちなみにPadは以下のようなやつです。間違ってたらごめんなさい。

f:id:crocus7724:20170520015028p:plain

今回はサンプルとしてビルドした回数とデバッグした回数を記録しておくPadを作ります。
(本当はRunした回数を取りたかったけど取得の仕方がわからなかった・・・)

PadContentの作成

まず最初にPadContentを継承したクラスを作成します。これはiOS/AndroidでいうUIViewController/Activityみたいな役割で、表示するViewを組み立てたりします。たぶん

実際にコードを見てみましょう。

using System;
using Gtk;
using MonoDevelop.Components;
using MonoDevelop.Debugger;
using MonoDevelop.Ide;
using MonoDevelop.Ide.Gui;
using MonoDevelop.Projects;

namespace SamplePadExtension
{
    /// <summary>
    /// ビルドした回数とデバッグ実行した回数を表示するPad
    /// </summary>
    public class SamplePadExtensionPad : PadContent
    {
        // デバッグした回数
        private int debugCount;

        // ビルドした回数
        private int buildCount;

        // デバッグした回数を表示するラベル
        private Label debugCountLabel;

        // ビルドした回数を表示するラベル
        private Label buildCountLabel;

        // 実際に表示するものを指定するバッキングフィールド
        // overrideのControlプロパティがget onlyなので
        private Control control;

        /// <summary>
        /// 実際に表示するControl(View)を格納するプロパティ
        /// </summary>
        public override Control Control => control;

        /// <summary>
        /// 実際に表示されるときに呼ばれる?
        /// </summary>
        /// <param name="window">Padのウィンドウ. Toolbarとかとれる</param>
        protected override void Initialize(IPadWindow window)
        {
            buildCountLabel = new Label {Text = $"Build count: {buildCount}"};
            debugCountLabel = new Label {Text = $"Debug count: {debugCount}"};

            // 縦積みするレイアウト
            var vBox = new VBox {buildCountLabel, debugCountLabel};
            // 表示
            vBox.ShowAll();
            control = vBox;

            IdeApp.ProjectOperations.StartBuild += OnStartBuild;
            DebuggingService.DebugSessionStarted += OnDebugSessionStarted;
        }

        // ビルドが始まったときに呼ばれる
        private void OnStartBuild(object sender, BuildEventArgs args)
            => buildCountLabel.Text = $"Build count: {++buildCount}";

        // デバッグ実行が始まったときに呼ばれる
        private void OnDebugSessionStarted(object sender, EventArgs eventArgs)
            => debugCountLabel.Text = $"Debug count: {++debugCount}";

        public override void Dispose()
        {
            IdeApp.ProjectOperations.StartBuild -= OnStartBuild;
            DebuggingService.DebugSessionStarted -= OnDebugSessionStarted;

            base.Dispose();
        }
    }
}

注意点なのですが、デバッグの開始を取得するためにMonodevelop.Debuggerを使っています。
デフォルトだと参照できないのでVisual Studio for Mac ExtensionプロジェクトのAddin Reference(下の画像の赤枠のやつ)をダブルクリックして表示されるポップアップからMonodevelop.Debuggerを選択してください。

f:id:crocus7724:20170520020303p:plain

Manifest.addin.xmlの編集

次に上で書いたPadContentを継承したクラスをVisual Studio for Mac Extensionが認識できるようにします。 Propertiesフォルダの中にあるManifest.addin.xmlファイルを以下のように編集します。

<?xml version="1.0" encoding="UTF-8"?>
<ExtensionModel>
    <!-- MainMenu→View→PadsにPadを追加する -->
    <Extension path="/MonoDevelop/Ide/Pads">
        <!-- 実際に追加するPad -->
        <!-- idは固有識別子 -->
        <!-- defaultPlacementは表示させたときのデフォルトの位置 -->
        <!-- classはPadContentを継承したクラスの完全修飾名 -->
        <!-- _labelはPadsに追加されたときに表示される文字 -->
        <Pad id="SamplePadExtension.SamplePadExtensionPad"
             defaultPlacement="Bottom"
             class="SamplePadExtension.SamplePadExtensionPad"
             _label="Sample Extension Pad"/>
    </Extension>
</ExtensionModel>

これで準備は完了です!

実行

それでは実際に実行してみましょう。

f:id:crocus7724:20170520021013p:plain

実行すると上のようなのが追加されていて、ビルドしたりデバッグしたりすると下のようにカウントが増えていくはずです。

f:id:crocus7724:20170520021057p:plain

Xamarin StudioまではファイルテンプレートにGtkがあったのですが、VS for MacからGtkテンプレートが消失してしまい、Xamarin StudioはVS for Macに置き換わる予定ですからGtkファイル手作業作成しか残されてないかなーと思うのですが辛すぎるので手抜きViewです。
もし凝ったレイアウトを作成したい場合は覚悟してください私はやりたくない

もうちょっと詳しく

もう少し凝ったレイアウトやよくあるリスト形式のPadを作成したい場合はおそらくBreakpointのPad実装である

monodevelop/BreakpointPad.cs at master · mono/monodevelop · GitHub

が参考になると思います。

まとめ

Visual Studio for Mac ExtensionならPadの作成も見た目を気にしなくていいならとても簡単にできます。
Padが作成できるとやれることの幅が広がるのでぜひ作ってみてください。

Visual Studio for Mac Extensionの作り方

Visual Studio for Macが正式リリースされたっぽい?のでVisual Studio for Macの拡張の作り方を調べてみました。(Xamarin Studio Addinと変わってなかったけど)

Addin Makerインストー

最初にVS for Mac ExtensionのテンプレートやデバッグサポートをしてくれるAddin Makerを入れます。

VS for Macを開き、Main MenuのVisual Studio→Extensions…を選択します。

f:id:crocus7724:20170513161535p:plain

そして開いたウィンドウのGallery→Addin Development→Addin Makerを選択しインストールします。

f:id:crocus7724:20170513161923p:plain

いつものようにFile→New Solution…を開き、Other→Miscellaneous→Generalに以下のようなものが追加されていたらインストール成功です。

f:id:crocus7724:20170513162226p:plain

_人人人人人人人人人人人人人人_
> Xamarin Studio Addin <
 ̄Y^Y^Y^Y^Y^Y^Y^Y^Y^Y^Y ̄

Extension作成

ここではサンプルとしてメニューから選択するとConsole.WriteLine("Hello World");を挿入するExtensionを作りたいと思います。(誰得だけど)

まずNew SolutionでXamarin Studio Addinを選択し、プロジェクトを作成します。
なおAddin Makerをインストールして最初の数回はよくアセンブリが見つからなかったりするエラーが発生しますが、VS for Macを再起動したりビルドしたりするとそのうちなおります。

次にメニュー等から選択されたときに実行されるCommandHandlerを作成し以下のように記述します。

using MonoDevelop.Components.Commands;
using MonoDevelop.Ide;

namespace SampleVSExtension
{
    /// <summary>
    /// コマンドが選択されると現在のキャレット位置に
    /// <code>Console.WriteLine("Hello World");</code>が挿入される
    /// コマンドハンドラー
    /// </summary>
    public class HelloWorldCommandHandler : CommandHandler
    {
        /// <summary>
        /// メインメニューなどが開かれるときなどに実行される
        /// ここで可視性や非活性、動的な文字の変更などができる
        /// </summary>
        /// <param name="info">コマンドのステータス</param>
        protected override void Update(CommandInfo info)
        {
            // ActiveDocument(現在開いているファイル)がnullではない(なにかしらのファイルを開いている)
            // ときにコマンドを表示
            info.Visible = IdeApp.Workbench.ActiveDocument != null;
        }

        /// <summary>
        /// コマンドが実際に選択されたときに実行される
        /// </summary>
        protected override void Run()
        {
            // 現在開いているエディタの情報を取得
            var editor = IdeApp.Workbench.ActiveDocument.Editor;
            // 現在のキャレット位置にConsole.WriteLine("Hello World");
            // を表示
            editor.InsertAtCaret("Console.WriteLine(\"Hello World\");");
        }
    }
}

もしusing MonoDevelopとかでエラーが表示されたらVS for Macを一回閉じてもう一度起動してみてください。

細かい説明などはソースコードに書いたので省略して次にCommandの種別を決めるSampleVSExtensionCommandsを作成します。

namespace SampleVSExtension
{
    public enum SampleVSExtensionCommands
    {
        InsertHelloWorld,
    }
}

これはただ単に種別を表すだけなのでとくに説明はありません。

最後に実際にどのような拡張をするかをVS for Macに知らせるManifest.addin.xmlを編集しましょう。
Manifest.addin.xmlAssemblyInfo.csなどがあるPropertiesフォルダの中にあります。

以下のように定義してみましょう。

<?xml version="1.0" encoding="UTF-8"?>
<ExtensionModel>
    <!-- Editという種類のコマンドを定義する -->
    <Extension path="/MonoDevelop/Ide/Commands/Edit">
        <!-- idは用意した種別を表すenumを完全修飾名({namespace}.{enum}.{value})で指定 -->
        <!--_labelはコマンドが表示される文字 -->
        <!-- defaultHandlerはCommandHandlerを拡張したクラスを完全修飾名で指定 -->
        <Command id="SampleVSExtension.SampleVSExtensionCommands.InsertHelloWorld"
                 _label="Insert Hello World"
                 defaultHandler="SampleVSExtension.HelloWorldCommandHandler"/>
    </Extension>
    <!-- MainMenuのEditに表示するコマンドを定義する -->
    <Extension path="/MonoDevelop/Ide/MainMenu/Edit">
        <!-- 上で定義したコマンドのidを指定 -->
        <CommandItem id="SampleVSExtension.SampleVSExtensionCommands.InsertHelloWorld"/>
    </Extension>
</ExtensionModel>

これで準備は完了です。実際に実行してみましょう!

MainMenuのEditにInsert Hello Worldが追加されていて

f:id:crocus7724:20170513171210p:plain

選択したら現在の行に挿入されたら成功です!

f:id:crocus7724:20170513171321p:plain

何故かキャレット位置ではなく行の頭に挿入されているのが謎というかバグですが気にしたら負けです。これで無事やりたいことはできました!!

他の機能の作り方

Visual Studio for Mac Extensionは参考になるものがたくさん公開されています。

私の場合はEditなどのソースコードを編集する機能などは以下を参考にしましたし、

monodevelop/main/src/addins/CSharpBinding at master · mono/monodevelop · GitHub

プロジェクトテンプレートなどの作り方は以下が参考になりました。

monodevelop/main/src/addins/AspNet at master · mono/monodevelop · GitHub

そしてこれらは以下にまとまっています。

monodevelop/main/src/addins at master · mono/monodevelop · GitHub

素晴らしいですね(((

まとめ

VS for MacのPreviewが外れ、.NET CoreやC#7対応を含めてXamarin Studioから徐々に移行してくると思います。
VS for MacのExtensionもXamarin Studio addinとあまり変わってない(たぶん)と思うのでぜひ作ってみてください!!

RiderでXamarin.Androidがサポートされました!!

RiderのPublic EAP20でXamarin.Androidがサポートされました 🎉🎉🎉🎉🎉
(ついでにT4テンプレートとNode.js)

さっそく試してみましょう。

Androidプロジェクト作成

Riderが起動したらNew solution or projectを選択します。

f:id:crocus7724:20170415001848p:plain

Bindings Libraryプロジェクトまである・・・!!
ネイティブバインディングまでRiderだけでできるかはあとで調べるとして、とりあえずBlank Appを選択してみます。

f:id:crocus7724:20170415002343p:plain

初期状態はこんな感じ。

さっそくデバッグしてみます。

f:id:crocus7724:20170415002535p:plain

シミュレータを適当に選んで・・・

f:id:crocus7724:20170415002638p:plain

画面真っ黒(´・ω・`)
無事に実行できました!!

f:id:crocus7724:20170415003555p:plain

ちなみにブレークポイントでもちゃんと止まります。

f:id:crocus7724:20170415014234p:plain また、Xamarin.FormsのAndroidプロジェクトも実行できました!!
(Xamarin.iOSがloadしてある状態だとビルドエラーになったのでわざとunloadしてます)

これは捗りますね!!

注意点

ただRider自体がまだPublic EAPな上にXamarin.Androidに対応したばっかりなので結構怪しいところがあります。

Riderで作成したXamarin.AndroidプロジェクトがXamarin Studioで開けない

開けません。

f:id:crocus7724:20170415005225p:plain

ただエラー内容はただ単にXamarin StudioはそんなProject Type GUIDなんて知らないよって言っているだけなので書き換えればOKです。

Xamarin.Androidの.csprojの

<ProjectTypeGuids>{02CD1F06-00EA-420C-944B-AD8BE8AD9848}</ProjectTypeGuids>

<ProjectTypeGuids>{EFBA0AD7-5A72-4C68-AF49-83D382785DCF};{FAE04EC0-301F-11D3-BF4B-00C04F79EFBC}</ProjectTypeGuids>

にすれば

f:id:crocus7724:20170415005627p:plain

Xamarin Studioでも開けました!!

ただ個人的にはまだXamarin Studioで作成してからRiderで開く運用のほうがいいと思います。(書き換えよろしくないと思いますし)

C#7使えない?

RiderはC#7に対応しているのですが、Xamarin Studioで作成したプロジェクトだと残念ながらコンパイル時にエラーになってしまいます。

ただ、Visual Studio for Macで作成したプロジェクトはRiderでもC#7で実行できるので、ヒトバシラーにはおすすめです() f:id:crocus7724:20170415022758p:plain




今回のバージョンアップでRiderでもXamarin.Androidプロジェクトが実行できるようになりました!
今回は適当なサンプルプロジェクトを実行しただけでこれが実用に耐えうるかは未知数ですが、これは大きな前進だと思います。また、Xamarin.iOSに関しても

ということで期待ですね・・・!!

Xamarin.Forms上でHello World

これは[学生さん・初心者さん大歓迎!]Xamarin Advent Calendar 2016 - Qiitaの23日目の記事です。

前日は

普段Swift書いてますがXamarinに入門してみました | Developers.IO

でした。

今回、本当はXamarin Studio Addinについて書こうとしたのですが、Xamarin Studioにブチ切れた諸々の諸事情により書く時間が足りなかったので思いっきりネタに走りました。本当に誰得レベル。

最近Xamarin.Forms書いてないし(専らNativeのほう)、そういえば私も1本もアプリ作れてないので初心者だと思います。なので、入門に向けてXamarin.Forms上でHello Worldをやってみました。
目指すのは以下のような感じ。

f:id:crocus7724:20161223225732p:plain

大事なことなのでもう一度いいます。ネタです。

環境

  • macOS Sierra
  • Xamarin Studio 6.1.2
  • Xamarin Forms 2.3.1.114

Hello World!!

とりあえずXamarin StudioでちゃちゃっとXamarin FormsのPortableでプロジェクトを作成します。(今考えるとSharedのほうが良かった)

作成したらぱぱっとHello Worldを実行したいのですが、PortableライブラリではNuGetを追加できませんでした。なので実行するためのインターフェースとして以下をPortableライブラリに追加します。

using System.Threading.Tasks;

namespace HelloForms
{
    public interface IScripting
    {
        Task<T> RunAsync<T>(string code);
    }
}

またNative側のNuGetパッケージにMicrosoft.CodeAnalytics.CSharp.Scriptingを追加します。 そして先程のインターフェースを実装したクラスを作成します。

using System.Threading.Tasks;
using HelloForms.iOS;
using Microsoft.CodeAnalysis.CSharp.Scripting;
using Xamarin.Forms;

[assembly: Dependency(typeof(Scripting))]

namespace HelloForms.iOS
{
    public class Scripting : IScripting
    {
        public async Task<T> RunAsync<T>(string code)
        {
            var result = await CSharpScript.RunAsync<T>(code);

            return result.ReturnValue;
        }
    }
}

上の[assembly:Dependency(typeof(Scripting))]はPortableライブラリ側でこのクラスを使うために必要です。 Tで戻り値の型を指定できるのですが今回全く使ってませんでした。(なくてもよかった)

これで準備は整いました。あとはPortableライブラリ側でViewとなんちゃってViewModelを作り・・・

  • HelloFormsPage.xaml
<?xml version="1.0" encoding="utf-8"?>
<ContentPage xmlns="http://xamarin.com/schemas/2014/forms"
             xmlns:x="http://schemas.microsoft.com/winfx/2009/xaml"
             xmlns:local="clr-namespace:HelloForms"
             x:Class="HelloForms.HelloFormsPage">
    <ContentPage.Padding>
        <OnPlatform x:TypeArguments="Thickness"
                    iOS="0,20,0,0"
                    Android="0"
                    WinPhone="0" />
    </ContentPage.Padding>
    <ContentPage.BindingContext>
        <local:HelloFormsPageViewModel />
    </ContentPage.BindingContext>
    <Grid>
        <Grid.RowDefinitions>
            <RowDefinition Height="5*" />
            <RowDefinition Height="*" />
            <RowDefinition Height="5*" />
        </Grid.RowDefinitions>

        <Editor Text="{Binding Code,Mode=OneWayToSource}" />
        <Button Grid.Row="1" Text="Run Script"
                Command="{Binding ExecuteCommand}" />
        <Editor Grid.Row="2" Text="{Binding Result}" />
    </Grid>
</ContentPage>
  • HelloFormsPageViewModel
using System;
using System.ComponentModel;
using System.Runtime.CompilerServices;
using Xamarin.Forms;

namespace HelloForms
{
    public class HelloFormsPageViewModel : INotifyPropertyChanged
    {
        private string _code;
        private string _result;

        public string Code
        {
            get { return _code; }
            set
            {
                _code = value;
                OnPropertyChanged();
            }
        }

        public Command ExecuteCommand { get; }

        public string Result
        {
            get { return _result; }
            set
            {
                _result = value;
                OnPropertyChanged();
            }
        }

        public HelloFormsPageViewModel()
        {
            ExecuteCommand = new Command(async () =>
            {
                try
                {
                    var result = await DependencyService.Get<IScripting>().RunAsync<object>(Code);
                    Result = result?.ToString() ?? "";
                }
                catch (Exception e)
                {
                    Result = e.Message;
                }
            });
        }

        public event PropertyChangedEventHandler PropertyChanged;

        protected virtual void OnPropertyChanged([CallerMemberName] string propertyName = null)
        {
            PropertyChanged?.Invoke(this, new PropertyChangedEventArgs(propertyName));
        }
    }
}

実行すれば・・・

f:id:crocus7724:20161220000511g:plain

Hello Worldができました!!さらにIDE側のApplication OutputにもHello Worldできます。
これで無事私も入門できました!

なにか違う

思えば確かにHello Worldと出力されたけどあれは真のHello Worldでは無い気がします。私が、いや私達が目指す真のHello Worldはまっさらな画面の真ん中にシステム標準の大きさで表示されているHello Worldなはずです。

というわけで改造。各プラットフォーム毎のScripting.RunAsync<T>の中身を以下のように変更

public async Task<T> RunAsync<T>(string code)
{
    var result = await CSharpScript.RunAsync<T>(code,ScriptOptions.Default
        .AddReferences(Assembly.Load("Xamarin.Forms.Core"),
            Assembly.Load("Xamarin.Forms.Platform"),
            Assembly.Load("Xamarin.Forms.Platform.iOS"),
            Assembly.Load("Xamarin.Forms.Xaml"))
        .AddImports("Xamarin.Forms"));
    return result.ReturnValue;
}

Xamarin.Forms関連のアセンブリを片っ端から読み込ませます。ここいらへんヤケクソ これで実行時にXamarin.Forms関連のアセンブリが参照と、using Xamarin.Formsの省略ができます。

これで完成。

いちいち入力するの怠いので以下のコードをクリップボードにコピーしといて・・・

Application.Current.MainPage.Navigation.PushAsync(new ContentPage
{
    Content = new StackLayout
    {
        VerticalOptions = LayoutOptions.Center,
        HorizontalOptions = LayoutOptions.Center,
        Children = { new Label
        {
            Text = "Hello World!"
        }}
    }
})

実行時貼り付けてやれば・・・

f:id:crocus7724:20161221222342g:plain

やりました!!真のHello Worldができました!!

違う

よくよく考えなくてもXamarin.FormsのViewはXAMLで組み立てるのが可読性的な意味でベストです。
なので正真正銘のHello WorldXAMLを使って表示させるべきなのではないでしょうか??

というわけでまたまた改造していきます。
まず、XAMLで書かれた文字列をインスタンスに変えなければならないのですが、自力で実装するのは骨が折れます。
幸いなことに、文字列からインスタンスに変換してくれるメソッドはXamarin.Formsにはあります。

https://github.com/xamarin/Xamarin.Forms/blob/master/Xamarin.Forms.Xaml/ViewExtensions.cs

このExtensionsクラスのLoadFromXamlメソッドの第2引数がstringのものがまさに求めていたものなのですが、非常に残念なことにアクセシビリティinternalです。

なので通常の方法で呼び出すことができません。。。非常に残念なので

using System;
using System.Linq.Expressions;

namespace HelloForms
{
    public static class Extensions
    {
        private static readonly Func<object, string, object> loadFromXamlDelegate;

        static Extensions()
        {
            // object view;
            var view = Expression.Parameter(typeof(object), "view");
            // string xaml;
            var xaml = Expression.Parameter(typeof(string), "xaml");

            // (view, xaml) => Xamarin.Forms.Xaml.Extensions.LoadFromXaml<view.Type>(view, xaml)
            var lambda = Expression.Lambda<Func<object, string, object>>(
                Expression.Call(typeof(Xamarin.Forms.Xaml.Extensions),
                    nameof(Xamarin.Forms.Xaml.Extensions.LoadFromXaml),
                    new[] {view.Type},view, xaml),
                view, xaml);

            loadFromXamlDelegate = lambda.Compile();
        }

        public static TXaml LoadFromXaml<TXaml>(this TXaml view, string xaml)
            => (TXaml) loadXamlDelegate.Invoke(view, xaml);
    }
}

リフレクションで呼び出すことにしました。
毎回リフレクション叩くのは芸がないなーということと通常のリフレクションだとうまくいかなかったので式木を使ってデリゲートを生成しています。 すごい便利ですね。式木。なおこれ式木でやる意味がない気がしますが、気がするだけで気のせいです。

あとはこいつを呼び出してあげればいいわけです。

RunAsyncHelloFormsの参照を追加してあげて

public async Task<T> RunAsync<T>(string code)
{
    var result = await CSharpScript.RunAsync<T>(code, ScriptOptions.Default
            .AddReferences(Assembly.Load("Xamarin.Forms.Core"),
                Assembly.Load("Xamarin.Forms.Platform"),
                Assembly.Load("Xamarin.Forms.Platform.iOS"),
                Assembly.Load("Xamarin.Forms.Xaml"),
                Assembly.Load("HelloForms"))
            .AddImports("Xamarin.Forms","HelloForms"));
    return result.ReturnValue;
}

実行時にいちいち書くのは面倒なので以下のコードをクリップボードにあらかじめコピーしといて

var page = new ContentPage().LoadFromXaml(@"<?xml version=""1.0"" encoding=""utf-8""?>
<ContentPage xmlns=""http://xamarin.com/schemas/2014/forms""
             xmlns:x=""http://schemas.microsoft.com/winfx/2009/xaml""
             xmlns:local=""clr-namespace:HelloForms""
             x:Class=""HelloForms.HelloFormsPage"">
    <StackLayout VerticalOptions=""Center"" HorizontalOptions=""Center"">
        <Label Text=""Hello World!!"" />
    </StackLayout>
</ContentPage>");

実行してあげれば

f:id:crocus7724:20161223191825g:plain

やりました!XAMLでHelloWorldに成功しました!!

今回は時間がなかったのでnew ContentPage().LoadFromXaml("{XAML}")インスタンス化しましたが、ちゃんとXAML用のエディタを用意してあげればいつもと同じような感じでアプリケーション上でXamarin.Formsを使ってMVVMが試せますね。凄い。補完がないので地獄ですが。

ようするに

Roslynです。 詳しくは以下が参考になります。

C#スクリプト実行 - C# によるプログラミング入門 | ++C++; // 未確認飛行 C

Roslyn for Scriptingで、あなたのアプリケーションにもC#スクリプトを!! – kekyoの丼

アプリケーションのマクロ的な言語にC#が使えます。
これでどこでもC#が書ける!ということで詳しく説明する記事を書こうと思ったのですが...断念。

理由の1つ目がNuGet。追加する前はほとんどない(実際Xamarin.Formsだけ)NuGetパッケージが

f:id:crocus7724:20161220003553p:plain

Microsoft.CodeAnalytics.CSharp.Scripting追加後・・・

f:id:crocus7724:20161220003626p:plain

こうなりました。すごい(小並感)

また、ScriptingはiOSでは使えません。シミュレータ動いてんじゃんと思うかもしれませんが、

f:id:crocus7724:20161221065702p:plain

ハイ。JITコンパイルじゃないと使えないらしいです(シミュレータはJITコンパイル、実機はAOTコンパイル)。なのでJITコンパイルが使えないiOSと、最近はAndroidもAOTですかね??まあモバイル勢はいろいろ制約がキツそうです。無念。

じゃあXamarin.Macは?と思うかもしれませんが、Target FrameworkがMobileだとSystem.Runtime.LoaderSystem.NotImplementedExceptionが出てScriptが実行できず、.NET4.5だとそもそもインストールできませんでした。

そもそもSystem.Runtime.Loaderが謎い。iPhoneシミュレータだとSystem.Runtime.Loaderがなくても実行できる(むしろあるとNotImplemented)のですが、iPhone実機で実行しようとするとコンパイルエラー。うーん、コンパイルの仕方で必要なアセンブリが変わるんですかね。。ここいらへんの仕組みがよくわかってないです。

というわけでXamarinだと今のところUWP?とAndroid(JIT)?とiPhoneシミュレータ上だけです。おしい、惜しい。
ただ前までは式木もできなかったらしいので今後Scriptingもできるようになることを期待してます。

以上、ネタでした。

明日の担当はnobukuma - Qiitaさんになります。よろしくお願いします!

MacでもRiderを使って快適にXamarinで開発しよう!!

これは

[学生さん・初心者さん大歓迎!]Xamarin Advent Calendar 2016 - Qiita

の7日目の記事です。

前日は

qiita.com

でした。
大学の講義でJavaやって2年目でServletその後Xamarinの流れとか完全に「自分かな?」と思いましたがこの話はまたいずれ。

今回はモバイルどころかIDEでほぼMac向けのお話ですがタイトルに(無理やり)Xamarinが入ったのでセーフ

祝!!Rider Public EAP Release!!

2016/11/22、RiderがPublic EAPになりました!!
長かった・・・

今回はそんなRiderの紹介です!

Riderとは

RiderはJetBrainsが絶賛開発中のクロスプラットフォームC# IDEです。詳しくは以下をお読み下さい。

Project Rider – 新しい C# IDE #jetbrainsrider | JetBrains ブログ

簡単に言うとJavaなどの開発環境でお馴染みのIntelliJ Platformで動作し、バックエンドではC#で書かれたReSharperが動作しています。

つまりは最強。

Riderの対応状況

最初にXamarinプロジェクトで使うに当たってのRiderの対応状況を見ていきたいと思います。現在のRiderの対応状況は以下のとおりです。

機能 対応状況 備考
プロジェクト読み込み UWPとかは無理(遠い未来対応するかも?)
ビルド 私はなぜかできるけどできない人もいる?
実行
デバッグ
コーディングスタイル 近いうちにきそう
XAML ハイライトが残念だけどReSharperと遜色ない補完
F# こんなに投票されてるのだから対応されないはずがない!
NuGet Xamarin Studioより高機能だけど若干怪しいところがある
ReSharperの機能
(補完やpostfixなど)
だいぶ対応したけど細かいところがまだ
IntelliJ Platformの機能
(Gitや各種プラグインなど)
普段使いには全く問題なく感じる

あくまで主観なので間違いがあったらコメントをぜひ。
コーディングスタイルは裏道がありそうですがそれは後述。

XamarinでRiderを使うにあたり、実行・デバッグはできません。なのでRider単体での開発は不可能です。
よってRiderでコーディング→Xamarin Studioでビルド・実行になります。

面倒くさいし切り替え忘れてXamarin Studioで編集してイライラする事もありますが、それでもRiderでコーディングすると圧倒的に書きやすいです。

始め方

そんな超有望なRiderをさっそくインストールしていきましょう!
インストール方法は2パターンあって www.jetbrains.com

からダウンロードしていつも通りインストールする方法とJetBrains Toolboxを使ってインストールする方法があります。Toolboxのダウンロードは以下からできます。

www.jetbrains.com

個人的には他のJetBrains製品を使っているならToolboxを利用したほうが楽だと思います。

初期設定

前回とほぼ変わりませんが初期設定について。

Riderを初めてインストールして起動するとおそらく以下の画面が表示されます。

f:id:crocus7724:20161127101303p:plain

前のバージョンから設定をインポートする?って聞かれますが初めてなのでデフォルトのままOK。
次はテーマを聞かれます。

f:id:crocus7724:20161127101441p:plain

個人的にダークテーマが好きなのでDarcula一択。
お次はエディターのカラーテーマです。左から独自、Visual Studio風(あくまで風)、IntelliJ風(というかそのまま)になります。お好きなものをどうぞ。

f:id:crocus7724:20161127104435p:plain

次にキーマップを選択できます。Visual Studio風とReSharperIntelliJ(macOS)から選択できます。

f:id:crocus7724:20161127104447p:plain

なおキーマップはあとで設定から下記も選べるようになります。

キーマップが選択し終わったらお次はDefault Pluginsです。私はいつも全部Enableのままにするのですが、もし「絶対使わねーよ!」という機能があれば無効にすれば起動が少し早くなるかも?

f:id:crocus7724:20161127104523p:plain

そして次におすすめっぽいPluginsがインストールできます。

f:id:crocus7724:20161127104513p:plain

IdeaVimは私は使ったことないのでなんとも言えません。
ReSharper UnityではデバッグまでできるっぽいのでそのXamarin版マダーと常々思っていますがくる気配はまだありません。
Heap Allocations Viewerは前にも書きましたが、あると便利だと思います。ただnewとかまで表示するのは正直邪魔なので表示・非表示の設定ができればなーと思います。
最後のPython Community Editionは・・・なんでしょう?Pythonスクリプトが書けるらしいのですが果たしてC# IDEであるRiderで使う機会あるの?

欲しいと思ったPluginをインストールしたら最後に

f:id:crocus7724:20161127104059p:plain

で初期設定は終了です。
それではStart using Riderを押しましょう!

f:id:crocus7724:20161127104217p:plain

これで今日からあなたもRiderを使い始めることができます。お疲れ様でした!!

機能

本当は機能をいろいろ紹介していきたいのですが確実に長文駄文になるので泣く泣く諦めます。。。が、せっかく調べたので2点紹介致します。

Postfix

よくコーディングをしていると途中まで式を書いて「これ変数にしたいな」とか「これをリターンしたいな」と思うことがあるのですが、そのときに役立つのが「Postfix」です。変数名やクラス名のあとで.を入力して対象の文字を入力してTabを押すか補完で出てきたものを選択すると展開します。

現在Riderで使えるPostfixは以下のとおりです。

入力 展開後 備考
Foo.var var foo = new Foo();
Foo.new new Foo()
foo.return return foo; 戻り値と型が一致してるとき
foo.yield yield return foo;
foo.throw throw foo;
Foo.typeof typeof(Foo) クラス
foo.null if(foo==null) { }
foo.notnull if(foo!=null) {}
foo.switch switch(foo) {}
foo.not !foo bool型
foo.if if(foo) {} bool型
foo.else if(!foo) {} bool型
foo.while while(foo) {} bool型
foo.lock lock(foo) {} 参照型
foo.using using(foo) {} IDispose型
task.await await task Task型
list.for for (var i = 0; i < list.Count; i++) {} コレクションや配列など
list.forr for (var i = list.Count - 1; i >= 0; i--) コレクションや配列など
list.foreach foreach (var item in list){ } コレクションや配列など
foo.parse int.Parse(foo) string型
foo.tryparse int.TryParse(foo, ) string型
foo.arg Method(foo) *
foo.cast ((object)foo) *
foo.par (foo) *
foo.prop Foo = foo; *
プロパティを生成してくれる
foo.field _foo = foo; *
フィールドを生成してくれる
foo.sel foo *
fooが選択状態になる
foo.to target = foo; *
まずToString()が出てくるのでescで消してからTab

結構あります。幾つかは私も初めて知りました。個人的にfoo.nameofがほしいのですがnameofはC#でも特別扱いっぽいので難しいのかも。

基本的に型が一致してないと補完に出てきませんが、いくつか(たとえばifとか)は補完に出てこなくてもTabを押せば展開されます。
また、*がついているやつは補完には出てきません。たまに展開に失敗するのでおそらく正式対応していないと思います。

自作Live Templates

ReSharperのLive Templateはマクロが使えてものすごく便利です。ただ、現時点では自分でLive TemplateをRiderで作成できません。(Live Templateの項目はありますが、言語にC#がなくXamarin StudioのTemplateに毛が生えた程度)

これについて先日行われた?中の人へのQ&Aにてサポートされることは確定なのですが、回答の中で

Current workaround: create a template in Visual Studio + ReSharper, save it to solution-wide .dotSettings file, and Rider will pick it up.

とあります。
なるほど、.dotSettingsファイルがあればLive Template自体は動くらしいです。
私はVisual Studioには詳しくはないのですが、とりあえずVisual Studio + ReSharperで下記のようなLive Templateを作成しました。

f:id:crocus7724:20161206021451p:plain

あとはReSharperのTemplateウィンドウにあるExportボタンをクリックするとUser.DotSettingsファイルが吐き出されます。

それをMacに持ってきたら適当なソリューションと同じ階層にUser.DotSettingsファイルを置いて{ソリューション名}.sln.DotSettingsにリネームすれば・・・

f:id:crocus7724:20161206121450g:plain

展開できました!!(若干バグってるけど)
これでBindable Propertyが爆速で書けます!

なお.DotSettingsファイルの置き場所がわからなくて右往左往してましたが、mdfindで.DotSettingsを検索したところクローン後放置して久しいXamarin.Formsリポジトリに.DotSettingsファイルがあり、それを参考にしました。ありがとうございます。

ちなみに.csprojも.slnも.storyboardも手で編集する昨今、「GUIで編集できないなら手動で編集すればいいじゃない」なノリで.DotSettingsファイルを開いてみたら

<wpf:ResourceDictionary xml:space="preserve" xmlns:x="http://schemas.microsoft.com/winfx/2006/xaml" xmlns:s="clr-namespace:System;assembly=mscorlib" xmlns:ss="urn:shemas-jetbrains-com:settings-storage-xaml" xmlns:wpf="http://schemas.microsoft.com/winfx/2006/xaml/presentation">
    <s:Boolean x:Key="/Default/PatternsAndTemplates/LiveTemplates/Template/=02646611FBC11F46BCFB19BD6AE7495C/@KeyIndexDefined">True</s:Boolean>
    <s:String x:Key="/Default/PatternsAndTemplates/LiveTemplates/Template/=02646611FBC11F46BCFB19BD6AE7495C/Shortcut/@EntryValue">bindable</s:String>
    <s:String x:Key="/Default/PatternsAndTemplates/LiveTemplates/Template/=02646611FBC11F46BCFB19BD6AE7495C/Description/@EntryValue">Generate Bindable Property</s:String>
    <s:String x:Key="/Default/PatternsAndTemplates/LiveTemplates/Template/=02646611FBC11F46BCFB19BD6AE7495C/Text/@EntryValue">#region $name$ Bindable Property&#xD;
&#xD;
public static readonly BindableProperty $name$Property&#xD;
            = BindableProperty.Create(nameof($name$),typeof($type$),typeof($declearType$));&#xD;
&#xD;
public $type$ $name$&#xD;
{&#xD;
    get { return (($type$)GetValue($name$Property)); }&#xD;
    set { SetValue($name$Property, value); }&#xD;
}&#xD;
&#xD;
#endregion</s:String>
    <s:Boolean x:Key="/Default/PatternsAndTemplates/LiveTemplates/Template/=02646611FBC11F46BCFB19BD6AE7495C/Reformat/@EntryValue">True</s:Boolean>
    <s:Boolean x:Key="/Default/PatternsAndTemplates/LiveTemplates/Template/=02646611FBC11F46BCFB19BD6AE7495C/ShortenQualifiedReferences/@EntryValue">True</s:Boolean>
    <s:Boolean x:Key="/Default/PatternsAndTemplates/LiveTemplates/Template/=02646611FBC11F46BCFB19BD6AE7495C/Applicability/=Live/@EntryIndexedValue">True</s:Boolean>
    <s:Boolean x:Key="/Default/PatternsAndTemplates/LiveTemplates/Template/=02646611FBC11F46BCFB19BD6AE7495C/Scope/=C3001E7C0DA78E4487072B7E050D86C5/@KeyIndexDefined">True</s:Boolean>
    <s:String x:Key="/Default/PatternsAndTemplates/LiveTemplates/Template/=02646611FBC11F46BCFB19BD6AE7495C/Scope/=C3001E7C0DA78E4487072B7E050D86C5/Type/@EntryValue">InCSharpFile</s:String>
    <s:String x:Key="/Default/PatternsAndTemplates/LiveTemplates/Template/=02646611FBC11F46BCFB19BD6AE7495C/Scope/=C3001E7C0DA78E4487072B7E050D86C5/CustomProperties/=minimumLanguageVersion/@EntryIndexedValue">2.0</s:String>
    <s:Boolean x:Key="/Default/PatternsAndTemplates/LiveTemplates/Template/=02646611FBC11F46BCFB19BD6AE7495C/Field/=type/@KeyIndexDefined">True</s:Boolean>
    <s:Int64 x:Key="/Default/PatternsAndTemplates/LiveTemplates/Template/=02646611FBC11F46BCFB19BD6AE7495C/Field/=type/InitialRange/@EntryValue">1</s:Int64>
    <s:Int64 x:Key="/Default/PatternsAndTemplates/LiveTemplates/Template/=02646611FBC11F46BCFB19BD6AE7495C/Field/=type/Order/@EntryValue">0</s:Int64>
    <s:Boolean x:Key="/Default/PatternsAndTemplates/LiveTemplates/Template/=02646611FBC11F46BCFB19BD6AE7495C/Field/=name/@KeyIndexDefined">True</s:Boolean>
    <s:Int64 x:Key="/Default/PatternsAndTemplates/LiveTemplates/Template/=02646611FBC11F46BCFB19BD6AE7495C/Field/=name/InitialRange/@EntryValue">2</s:Int64>
    <s:Int64 x:Key="/Default/PatternsAndTemplates/LiveTemplates/Template/=02646611FBC11F46BCFB19BD6AE7495C/Field/=name/Order/@EntryValue">1</s:Int64>
    <s:Boolean x:Key="/Default/PatternsAndTemplates/LiveTemplates/Template/=02646611FBC11F46BCFB19BD6AE7495C/Field/=declearType/@KeyIndexDefined">True</s:Boolean>
    <s:String x:Key="/Default/PatternsAndTemplates/LiveTemplates/Template/=02646611FBC11F46BCFB19BD6AE7495C/Field/=declearType/Expression/@EntryValue">typeName()</s:String>
    <s:Int64 x:Key="/Default/PatternsAndTemplates/LiveTemplates/Template/=02646611FBC11F46BCFB19BD6AE7495C/Field/=declearType/InitialRange/@EntryValue">-1</s:Int64>
    <s:Int64 x:Key="/Default/PatternsAndTemplates/LiveTemplates/Template/=02646611FBC11F46BCFB19BD6AE7495C/Field/=declearType/Order/@EntryValue">2</s:Int64></wpf:ResourceDictionary>

諦めました。

また、この.DotSettingsファイルはコーディング規則的なのを決められるみたいですが、こちらで簡単にやってみたところ変更できませんでした。ただ、私自身.DotSettingsを理解してないのでもうちょっと調べてみます。

→Xamarin.Formsリポジトリから.DotSettingsファイルをパクってきたところ、private修飾子をつけたら「いらねえよ」と言われるようになりましたなんでや!

f:id:crocus7724:20161206131042p:plain

Xamarin.Formsで開発するときは本家に合わせる意味でXamarin.Forms.sln.DotSettingsファイルをコピペしてもいいのではないでしょうか?

なお、Riderのyoutrackによると、RiderでのCodeStyle作成 のプロトタイプは既にできていて、次のEAPで利用できるようになるらしいです。(Live Templateは?)

次のEAPが待ち遠しいですね。

終わりに

以上、Riderの紹介でした。現在RiderはPublic EAPであり、正式リリースはまだまだ先です(2017年第2四半期になるらしい?)。また細かなところでバグいですが、現時点でもReSharperの恩恵を多大に受けることができます。ぜひ使ってみて下さい!!

なお学生の皆さんは今回出てきたReSharperIntelliJなどのJetBrains製品が在学期間中ずっと無料で使えます!
詳細は以下

www.jetbrains.com

人気の言語は一通り網羅しているので大学の講義などでプログラミングをやるときに使ってみてはいかがでしょうか?

明日の担当はmmmmmiya - Qiitaさんです。よろしくお願いします(((o(゚▽゚)o)))♡