20191028のC#に関する記事は20件です。

【Xamarin】C#でAndroidアプリを作ってみる 6 (簡単なアニメーション)

概要

前回:C#でAndroidアプリを作ってみる 5 (見栄えの調整)
今回は、干支×星座算出結果表示に簡単なアニメーションを追加します。
生年月日選択→待機アニメーション→算出結果表示という流れです。

アニメーションさせる画像を追加する

「Resources\drawable」に画像を追加します。
この画像ファイル名をstar.pngとします。
activity_main.xmlに、以下のImageViewを追加します。

activity_main.xml
    <ImageView 
        android:layout_height="wrap_content"
        android:layout_width="wrap_content"
        android:id="@+id/star_image" 
        android:src="@drawable/star"
        android:visibility="gone"/>  

「visibility="gone"」で表示領域を確保せずに非表示にしています。

アニメーションを定義する

ソリューションエクスプローラーの「Resources」で右クリックし、追加→新しいフォルダを選択します。
フォルダ名を「anim」とします。
更に、「Resources\anim」で右クリックし、追加→新しい項目を選択します。
XMLファイルを選択します。
xml追加.PNG
ファイル名は「wait_animation.xml」としました。追加ボタンを押下します。
コードは以下の通りです。

wait_animation.xml
<?xml version="1.0" encoding="utf-8" ?>
<set xmlns:android="http://schemas.android.com/apk/res/android"
     android:interpolator="@android:anim/accelerate_interpolator">
  <translate android:duration="800"
             android:fromXDelta="0%p" 
             android:toXDelta="100%p"
             android:fromYDelta="0%p"
             android:toYDelta="100%p"/>
</set>

左上から右下に移動するアニメーションです。

アニメーションを開始する

アニメーションを開始するために、MainActivityに以下のコードを追加します。

MainActivity.cs
using Android.Views.Animations;

・・・省略・・・
        // 星の画像
        ImageView starImage;
・・・省略・・・

        protected override void OnCreate(Bundle savedInstanceState)
        {
            ・・・省略・・・
            starImage = FindViewById<ImageView>(Resource.Id.star_image);
        }

        /// <summary>
        /// 結果を表示する
        /// </summary>
        private void DisplayResults(DateTime time)
        {
            // 星画像を表示する
            starImage.Visibility = Android.Views.ViewStates.Visible;

            // アニメーション開始
            var waitAnimation = AnimationUtils.LoadAnimation(this, Resource.Animation.wait_animation);
            starImage.StartAnimation(waitAnimation);

            // アニメーション完了イベント
            starImage.Animation.AnimationEnd += (sender, e) => {

                // 星画像を非表示にする
                starImage.Visibility = Android.Views.ViewStates.Gone;

                selectedDateLabel.Text = time.ToString("yyyy年MM月dd日")
                            + $"\r\nあなたは、{GetEto(time.Year)}{GetSeiza(time.Month, time.Day)}です。";
            };              
        }

日付選択処理(DateSelectButton_Click)を以下のように変更します。

MainActivity.cs
        /// <summary>
        /// SELECT DATEボタン押下時の処理
        /// </summary>
        /// <param name="sender"></param>
        /// <param name="e"></param>
        private void DateSelectButton_Click(object sender, System.EventArgs e)
        {
            var frag = DatePickerFragment.NewInstance(DisplayResults);
            frag.Show(FragmentManager, DatePickerFragment.TAG);
        }        

実行してみる

kbuce-rq1zu.gif

まとめ

今回は、簡単なアニメーションを追加するところまでを行いました。
次回は、未定です。

  • このエントリーをはてなブックマークに追加
  • Qiitaで続きを読む

C#プログラム向きのPC

なるべくいいスペックにしましょう
Visual Studio はそれなりに重いです

CPU intel製がお勧め、i7かi9せめてi5、コスパが良くないのでi3はだめ、だったら廉価なセレロンとか最近あまり見かけなくなったAMDとかの方がまし

4コア以上

HDDでは無理です
容量少なめでもSDDです

メモリーは多い方がいいです
純正は高いので、メモリー交換が簡単な機種を選びあとで交換した方が安くつくかも
32GBできれば64Gb

間違っても、Appleは買わないよう
できるという意見もありますが、いろいろ手間だし、ダメなこともあります

  • このエントリーをはてなブックマークに追加
  • Qiitaで続きを読む

【Xamarin】C#でAndroidアプリを作ってみる 5 (見栄えの調整)

概要

前回:C#でAndroidアプリを作ってみる 4 (画面作成2)
今回は、デフォルトのままのコントロールの見栄えを整えます。

Buttonに画像を表示する

SELECT DATEボタンの背景に画像を表示します。
ソリューションエクスプローラーの「Resources\drawable」を選択します。
右クリックでメニューを表示し、追加⇒既存の項目を選択します。
ソリューションエクスプローラ.PNG
画像ファイルを選択して追加します。以降、ファイル名を仮にbutton.pngとします。

Buttonのtextを、Resources\values\strings.xmlに定義します。
ついでに、app_nameも変更してタイトルを変えます。

strings.xml
<resources>
    <string name="app_name">干支×星座算出アプリ</string>
    <string name="action_settings">Settings</string>
    <string name="date_select_button">誕生日を選択してね!</string>
</resources>

activity_main.xmlのSELECT DATEボタンを以下のように変更します。

activity_main.xml
    <Button
        android:text="@string/date_select_button"
        android:layout_width="wrap_content"
        android:layout_height="wrap_content"
        android:minWidth="25px"
        android:minHeight="25px"
        android:id="@+id/date_select_button"
        android:background="@drawable/button"
        android:layout_centerHorizontal="true"
        android:layout_marginTop="10px"
        android:gravity="top"/>

「layout_centerHorizontal="true"」で水平方向の真ん中にします。
「layout_marginTop="10px"」で上に余白を作ります。
「gravity="top"」で文字を上に移動します。

実行してみると、下図のようになります。
Button.png

算出結果のラベルを見やすくする

算出結果の文字を大きくして、真ん中に表示します。
TextViewを以下のように変更します。

activity_main.xml
    <TextView
        android:id="@+id/selected_date_label"
        android:layout_height="match_parent"
        android:layout_width="match_parent"
        android:layout_below="@id/date_select_button"
        android:gravity="center"
        android:textSize="24sp" />

「gravity="center"」で文字を真ん中に移動します。
「textSize="24sp"」で文字サイズを設定します。

実行してみると、下図のようになります。
算出結果の位置.png

まとめ

今回は、見栄えを改善するところまでを行いました。
次回は、算出結果を表示する際に、簡単なアニメーションを追加します。
次回分はこちら⇒C#でAndroidアプリを作ってみる 6 (簡単なアニメーション)

  • このエントリーをはてなブックマークに追加
  • Qiitaで続きを読む

初めてのVisualStudioでプロジェクト作る人に

プロジェクト
プログラムを作る一式を収める箱
もう少し大きい箱にソリューションがあります

詳しくは公式で(https://docs.microsoft.com/ja-jp/visualstudio/get-started/tutorial-projects-solutions?view=vs-2019)

Visual Studio を起動します
インストールが済んでいればメニューから起動できます

image.png
「新しいプロジェクトの作成」をクリック

image.png
まずは、コンソールアプリケーションを作ってみましょう
「コンソールアプリ(>NET Core)」をクリックして選んでから
次へのボタンをクリック

image.png
作成ボタンをクリック

image.png

実行ボタンを押すと
image.png

Hello World! と 出力されます
image.png

  • このエントリーをはてなブックマークに追加
  • Qiitaで続きを読む

【Unity】UnityにVivox導入してみた【Sound】

Vivox ってな~に?

vivox.png
https://www.vivox.com/partners
PUBG, Fortnite, LoL, R6S, WoT, など世界的に売れている超有名ゲームで用いられている
テキスト/ボイスチャット提供ミドルウェアです。
2018年9月頃よりインディーデベロッパーでも無償で使えるようになりました(一部条件有)

日本国内ではほとんど導入事例を聞きませんが、現在ボイスチャットを導入するなら
間違いなくこれという状態なので知っておいて損は無いでしょう。

Vivox 導入フロー

アカウント作成まで

  1. 公式サイト(https://developer.vivox.com/) に行ってSDKを手に入れる心構えをする(ここ大事)
  2. 氏名とメールアドレスを登録 (ちゃんと確認用メールが届いているかチェックしよう)
  3. 組織を入力
  4. 組織の審査を待つ

審査通過後~SDK 取得まで

  1. Planを設定
    基本的にはFreePlanでOK, 商用の場合は条件をよく読みましょう
    vivox_plan.PNG

  2. Sandbox アプリケーションを作成
    vivox_dashboard.PNG

  3. アプリケーションの詳細設定
    最悪間違えてもあとでアプリの編集から再設定できるので必要最低限で大丈夫です
    vivox_make_app_01.PNG
    今回はUnityでの開発なのでUnityを選択してリリースに必要なプラットフォームを選択しましょう
    vivox_make_app_02.PNG

  4. 左側メニューのDownloadからSDKを選択
    vivox_sdk.PNG
    Android を選択していますが、UnitySDKを選択すると中にStandalone用とAndroid用とiOS 用のSDKがUnityPackageとして入っていました。とても優しいですね。

  5. DLしてきたzip ファイルを解凍するとUnityCustomPackageがあるのでこれをUnityProject側にImport

ドキュメントについて

ReadMeには「ナレッジベースの~」という説明がありますが、日本語化UIだと場所がよくわからないので説明します。
1. Developperページのところのドキュメントを選択
2. バージョンを選択
3. General Unity を選択
vivox_doclink.PNG
4. ドキュメントページへ移動
5. Vivox Core Unity: Developer First Stepsを見ながら初期設定を行う

Unityプロジェクト側の初期設定

カスタムパッケージをImport後からの手順です。
1. Assets/Vivox/Samples/TanksUnityGameSample/Scenes/MainScene.unity を開く
2. VivoxのDevelopperPortalからDashBoard 内にある先ほど作ったアプリを選択
3. InspectorにあるVivoxVoiceManager にWebページに乗っているパラメータをセット
(画像はセキュリティの都合上値を一部非表示しています)
vivox_app_config.PNG
vivox_config.PNG
4. Unity側のBuildSettingsにSceneを設定してビルド
vivox_buildSettings.PNG

まとめ

だらだらやって2~3時間でボイスチャット/テキストチャットが動きました。
Pixel3, Oculus Go, UnityEditor(PC)間で簡単にボイチャが出来るのは驚くべきことで、
謳い文句の36時間以内にボイスチャットが動くはあながち間違いじゃないなと思いました。

今回は導入フローまでですが、製品版で使うには別途審査があったりしますが、
導入ハードルの低さや性能部分に関しては文句なしといったところでしょうか。

  • このエントリーをはてなブックマークに追加
  • Qiitaで続きを読む

はじめます

もうすぐ、義務教育でもプログラムが始まるんだよ!

プログラム言語って、C#だけじゃないけど?
プログラムを始めるには、一番いいと思うよ

C#たんの生みの親の岩さんも言ってるし
https://ufcpp.net/study/csharp/getstarted.html

まあ、色々なプログラム言語のことは、丸さんに聞いてね。
https://ncode.syosetu.com/n1081fh/

  • このエントリーをはてなブックマークに追加
  • Qiitaで続きを読む

C#でのプログラム初学者向けのチュートリアルを始めます

もうすぐ、義務教育でもプログラムが始まるんだよ!

プログラム言語って、C#だけじゃないけど?
プログラムを始めるには、一番いいと思うよ

C#たんの生みの親の岩さんも言ってるし
https://ufcpp.net/study/csharp/getstarted.html

まあ、色々なプログラム言語のことは、丸さんに聞いてね。
https://ncode.syosetu.com/n1081fh/

第0章 C#でプログラムをする前準備(https://qiita.com/CsharpTan/items/3db4373b924b7d1ab9ec)

  • このエントリーをはてなブックマークに追加
  • Qiitaで続きを読む

脱出ゲームの作り方 7 特定の条件でアイテムを使用

取得した電球を使うと電気が点灯する処理が作成されたが、
LightstandPanel以外の場所でも使えてしまうので、LightstandPanelの場面で電気を使うようにする

各パネルを表示するときに、現在のパネルが何かをチェックする

GameManagerで下記を追加する

◇変数currentPanelを列挙型で作成し、ROOMパネルで初期化する

public enum PANEL
{
    ROOM,
    LIGHT_STAND,
    DRAWER,
    PC
}

    // 現在表示しているパネル
    public PANEL currentPanel = PANEL.ROOM;

◇ボタンを押したら、現在のパネルをcurrentPanelに取得し、該当するパネルを表示する

バックボタンが押されたら、currentPanelをROOMに戻し、他のパネルを非表示にする

    // ボタンを押したら該当するパネルを表示
    public void OnClickLightStandTrigger()
    {
        currentPanel = PANEL.LIGHT_STAND;
        lightStandPanel.SetActive(true);
    }
    public void OnClickDrawerTrigger()
    {
        currentPanel = PANEL.DRAWER;
        drawerPanel.SetActive(true);
    }
    public void OnClickPCTrigger()
    {
        currentPanel = PANEL.PC;
        pcPanel.SetActive(true);
    }

    // ボタンを押したらパネルを全て非表示
    public void OnClickBackTrigger()
    {
        currentPanel = PANEL.ROOM;
        lightStandPanel.SetActive(false);
        drawerPanel.SetActive(false);
        pcPanel.SetActive(false);
    }


アイテムを電気スタンドが表示されている時だけ使用する

◇現在のパネルが電気スタンドのときに、アイテムを使う
ItemBoxManagerでGameManagerを取得し、UseItem()を書き換える

    [SerializeField] GameManager gameManager;   


 // アイテムの使用
    public void UseItem(int index)
    {
        if ( gameManager.currentPanel == PANEL.LIGHT_STAND
          && itemList[index] == ITEM.LIGHT_BULB)   //現在のパネルが電気スタンドで、かつ使用するアイテムが電球だったら
        {
            lightStandManager.LightSwitch(true);            //電気をつける
            itemList[index] = ITEM.NONE; // アイテムを使用したので空にする;
            itemBoxImages[index].sprite = null;
        }
    }

InspecterでItemBoxPnelオブジェクトのItemBoxManagerスクリプトにGameManagerが出ているので、GameManagerを設定する

:interrobang:
UseItem(int index)関数は、OnClick()ではなく実行できる?

教材

Unityゲームスタジオ スタジオしまづ
【Unity】初心者からの脱出!? 脱出ゲームの作り方 その7 特定の条件でアイテムを使用
https://youtu.be/ualq_a3rnQc

  • このエントリーをはてなブックマークに追加
  • Qiitaで続きを読む

値オブジェクトのnull対応ってNullObjectパターンで良いのでは?

はじめに

値オブジェクトの悩みポイントとどう付き合っていくかを見させていただいてのですが、
悩みがちな項目に

  • Nullableな値を取り扱う場合

がありました。
NullObjectパターンじゃないの?と自分は思っていたのですが、ネットで言及している情報があまりなかったのでここが良いよっていう点を投稿してみます。

業務知識

以下のようなドメインで考えます。

image.png

  • Nullを許容しない値オブジェクト(ユーザーID)
  • nullと空文字は別扱いしたい値オブジェクト(名前)
  • DateTimeとかNullにできない値オブジェクト(生年月日)
  • 選択肢系の値オブジェクト(性別種別)

を用意してみました。

前提

値オブジェクトの抽象クラスには値オブジェクトを実装するを使用しています。値オブジェクトの特性を満たせていればなんでも良いです。
値オブジェクトの生成はstaticファクトリメソッド派です。

Nullを許容しない値オブジェクト

例えばユーザーIDのようなNullを許容しない値オブジェクトは以下のようにガチガチに作れば良いと思ってます。

    public class ユーザーID : ValueObject
    {
        private Guid _id { get; }

        public string Guid文字列 => _id.ToString();

        public static ユーザーID CreateNew() => new ユーザーID(Guid.NewGuid());
        public static ユーザーID Create(object o)
        => o switch {
            null => throw new ArgumentException("Guidに変換出来ません。Nullです。", "_ユーザーID"),
            string s => new ユーザーID(文字列をGuidに変換(s)),
            Guid g => new ユーザーID(g),
            _ => throw new ArgumentException("Guidに変換出来ません。" + o.ToString(), "_ユーザーID"),
        };

        private static Guid 文字列をGuidに変換(string s)
        {
            Guid g;
            var result = Guid.TryParse(s, out g);

            return (result) ? g : throw new ArgumentException("Guidに変換出来ません。" + s, "_ユーザーID");
        }

        private ユーザーID(Guid _id) => this._id = _id;

        protected override IEnumerable<object> GetAtomicValues()
        {
            yield return _id;
        }
    }

nullと空文字は別扱いしたい値オブジェクト

例えば名前のように(今回想定しているシステムでは)必須ではない項目に関しては別途Null名前のように名前を継承したNullObjectを用意すれば使う側は動作の違いを意識する必要がなくなるはず。

    public class 名前 : ValueObject
    {
        protected string _名前 { get; }
        public virtual string 名前文字列 => _名前;
        public virtual string 名前様付 => _名前 + "様";

        public static 名前 Create(object _名前)
        => _名前 switch {
                string s when string.IsNullOrEmpty(s) => new Null名前(),
                string s when s.Length > 60 => throw new ArgumentException("名前は60文字以下", nameof(_名前)),
                string s => new 名前(s),
                _ => throw new ArgumentException("文字列以外です。" + _名前.ToString(), "_名前")
            };
        private 名前(string _名前) => this._名前 = _名前;
        protected 名前() => this._名前 = null;

        protected override IEnumerable<object> GetAtomicValues()
        {
            yield return _名前;
        }
    }

    public class Null名前 : 名前
    {
        public override string 名前文字列 => "";
        public override string 名前様付 => "";
    }

DateTimeとかNullにできない値オブジェクト

例えば生年月日のように(今回想定しているシステムでは)必須ではない項目に関しては別途Null生年月日のように生年月日を継承したNullObjectを用意すれば使う側は動作の違いを意識する必要がなくなるはず。
今回はDateTime.MinValueを有り得ない値として表現しました。DateTimeをNull許容型にして操作するのも有りかと。

    public class 生年月日 : ValueObject
    {
        protected DateTime _生年月日 { get; }

        public virtual string 生年月日スラッシュ => _生年月日.ToString("yyyy/MM/dd");
        public virtual string 生年月日ハイフン => _生年月日.ToString("yyyy-MM-dd");

        public static 生年月日 Create(object o)
        => DateTimeに変換(o) switch {
            null => new Null生年月日(),
            DateTime d when d.Equals(DateTime.MinValue) => new Null生年月日(),
            DateTime d when d > DateTime.Now => throw new ArgumentException("今日より未来の生年月日は不可", "_生年月日"),
            var d => new 生年月日(d.Value),
        };

        private static DateTime? DateTimeに変換(object o)
        => o switch {
            string s when string.IsNullOrEmpty(s) => null,
            string s => 文字列をDateTimeに変換(s),
            DateTime d => d,
            _ => throw new ArgumentException("DateTimeに変換出来ません。" + o.ToString(), "_生年月日"),
        };
        private static DateTime? 文字列をDateTimeに変換(string s)
        {
            DateTime? d = null;
            DateTime d2;
            var result = DateTime.TryParse(s, out d2);
            if (result) d = d2;

            return (result) ? d : throw new ArgumentException("DateTimeに変換出来ません。" + s, "_生年月日");
        }

        private 生年月日(DateTime _生年月日) => this._生年月日 = _生年月日.Date;
        protected 生年月日() => _生年月日 = DateTime.MinValue;

        protected override IEnumerable<object> GetAtomicValues()
        {
            yield return _生年月日;
        }
    }

    public class Null生年月日 : 生年月日
    {
        public override string 生年月日スラッシュ => "";
        public override string 生年月日ハイフン => "";
    }

選択肢系の値オブジェクト

例えば性別種別のように(今回想定しているシステムでは)必須ではない項目で、画面上の都合で選択していないという状態を表現するのにもNullObjectで良いかと。

    public enum 性別
    {
         = 1,
         = 2
    }

    public class 性別種別 : ValueObject
    {
        protected 性別? _性別 { get; }

        public virtual string 文字列 => Enum.GetName(typeof(性別), _性別.Value);
        public virtual int  => (int)(_性別.Value);

        public static 性別種別 Create(object o)
        => o switch {
            null => new 性別種別選択外(),
            int i when i == 0 => new 性別種別選択外(),
            var v => new 性別種別(性別に変換(v))
        };
        public static 性別 性別に変換(object o)
        => o switch {
            int i => (性別)Enum.ToObject(typeof(性別), i),
            string s => (性別)Enum.Parse(typeof(性別), s),
            _ => throw new ArgumentException("性別種別に変換できません。" + o.ToString(), "_性別")
        };
        public static 性別種別 Create() => Create(1);
        public static 性別種別 Create() => Create(2);
        public static 性別種別 Create選択外() => Create(null);

        private 性別種別(性別 _性別) => this._性別 = _性別;
        protected 性別種別() => _性別 = null;

        protected override IEnumerable<object> GetAtomicValues()
        {
            yield return _性別;
        }
    }

    public class 性別種別選択外 : 性別種別
    {
        public override string 文字列 => "選択外";
        public override int  => 0;
    }

    public class 性別種別一覧
    {
        private static List<性別種別> _一覧 { get; } = new List<性別種別>{
            性別種別.Create選択外(),
            性別種別.Create(),
            性別種別.Create(),
        };

        private static List<性別種別> _選択外無一覧 { get; } = new List<性別種別>{
            性別種別.Create(),
            性別種別.Create(),
        };

        public static List<性別種別> 一覧 => _一覧;
        public static List<性別種別> 選択外無一覧 => _選択外無一覧;
        public static List<(string, int)> 名称と値一覧 => _一覧.Select(x => (x.文字列, x.)).ToList();
    }

業務知識として選択なしというのを扱うのならば素直にenumに追加して上げても良いはず。

例えばプリミティブな値から復元するとき

プリミティブな値からユーザーを復元する際の例を書いてみます。

    public class ユーザー
    {
        public ユーザーID ID { get; }
        public 名前 名前 { get; }
        public 生年月日 生年月日 { get; }
        public 性別種別 性別種別 { get; }

        public ユーザー(ユーザーID _ID, 名前 _名前, 生年月日 _生年月日, 性別種別 _性別種別)
        {
            ID = _ID;
            名前 = _名前;
            生年月日 = _生年月日;
            性別種別 = _性別種別;
        }
    }

    public interface Iユーザーファクトリ {
        ユーザー Create(object _ID, object _名前, object _生年月日, object _性別種別);
    }

    public class ユーザーファクトリ : Iユーザーファクトリ
    {
        public ユーザー Create(object _ID, object _名前, object _生年月日, object _性別種別)
        => new ユーザー(
            ユーザーID.Create(_ID),
            名前.Create(_名前),
            生年月日.Create(_生年月日),
            性別種別.Create(_性別種別)
        );
    }

こんな感じでnullか否かを意識することなくすっきり書けます。許容外の値が来た時のエラーハンドリングは必要ですけど。

例えばDBに保存するとき

ユーザーをリポジトリを介して保存する例を書いてみます。

    public class ユーザーDAO
    {
        public string ID { get; set; }
        public string 名前 { get; set; }
        public string 生年月日 { get; set; }
        public int 性別種別 { get; set; }
    }

というようなDAOがあったとして

    public interface Iユーザーリポジトリ {
        void Save(ユーザー _ユーザー);
    }

    public static class なんかのフレームワーク向け拡張メソッド {
        public static string なんかのフレームワーク向け格納(this 名前 _名前) => _名前.名前文字列;
        public static string なんかのフレームワーク向け格納(this 生年月日 _生年月日) => _生年月日.生年月日ハイフン;
        public static int なんかのフレームワーク向け格納(this 性別種別 _性別種別) => _性別種別.;
        public static string なんかのフレームワーク向け格納(this ユーザーID _id) => _id.Guid文字列;
    }
    public class ユーザーリポジトリ : Iユーザーリポジトリ
    {
        private なんかのフレームワーク なんかのフレームワーク { get; }
        public ユーザーリポジトリ() => なんかのフレームワーク = new なんかのフレームワーク();

        public void Save(ユーザー _ユーザー)
        {
            ユーザーDAO ユーザーDAO = なんかのフレームワーク.Get(ユーザー.ID);
            ユーザーDAO.名前 = ユーザー.名前.なんかのフレームワーク向け格納();
            ユーザーDAO.生年月日 = ユーザー.名前.なんかのフレームワーク向け格納();
            ユーザーDAO.性別種別 = ユーザー.名前.なんかのフレームワーク向け格納();

            なんかのフレームワーク.Save(ユーザーDAO);
        }
    }

値を詰め込むときもNullか否かを意識せずに詰め込めます。
フレームワーク特有の詰め込み方がある場合には拡張メソッドなどで特有の処理を書いたほうが良いかと。
ドメインの方にフレームワーク特有の処理を書いてしまうと業務知識が汚染される気がします。

どうしてもNullか否かを分岐したいとき

その場合は

bool IsNull()

なんかを値オブジェクトに用意するのではなく

if (o is Null名前)

のように型判定で分岐しましょう。

まとめ

業務知識としてはnullは許したくなくてもDB等保存側の制限でNull許容にしなければならないときがあります。
その際はなあなあにせずきちんと業務知識として検討しないといけません。
(nullをそのまま代入するか、別値として内部では処理するか、とか)
その際にNullObjectとして型分けしておけば処理の差分が書きやすいかと思っています。

値オブジェクトのNullableな値を取り扱う場合についてNullObjectパターン以外で便利な方法があればご教示頂きたいです。
あと値オブジェクトのNullObjectパターンだとこの場合に対応出来ない、とか。

  • このエントリーをはてなブックマークに追加
  • Qiitaで続きを読む

【Xamarin】C#でAndroidアプリを作ってみる 4 (画面作成2)

概要

前回:C#でAndroidアプリを作ってみる 3 (画面作成1)
今回は、選択した日付の干支×星座算出部分を作ります。

干支を算出する

干支の求め方を参考に計算します。
MainActivity.csに以下のコードを追加します。

MainActivity.cs
        // 十二支
        private const string ETO = "申酉戌亥子丑寅卯辰巳午未";

        /// <summary>
        /// 干支の算出
        /// </summary>
        /// <param name="year"></param>
        /// <returns></returns>
        private string GetEto(int year)
        {
            return ETO[year % 12].ToString();
        }

星座を算出する

星占いの星座です。
MainActivity.csに以下のコードを追加します。

MainActivity.cs
using System.Linq;

        // 星座
        private static Tuple<string, int, int>[] SEIZA = {
            Tuple.Create("牡羊座", 321, 419),
            Tuple.Create("牡牛座", 420, 520),
            Tuple.Create("双子座", 521, 621),
            Tuple.Create("蟹座", 622, 722),
            Tuple.Create("獅子座", 723, 822),
            Tuple.Create("乙女座", 823, 922),
            Tuple.Create("天秤座", 923, 1023),
            Tuple.Create("蠍座", 1024, 1122),
            Tuple.Create("射手座", 1123, 1221),
            Tuple.Create("山羊座", 1222, 1231),
            Tuple.Create("山羊座", 101, 120),
            Tuple.Create("水瓶座",121, 218),
            Tuple.Create("魚座", 219, 320)
        };

        /// <summary>
        /// 星座の算出
        /// </summary>
        /// <param name="month"></param>
        /// <param name="day"></param>
        /// <returns></returns>
        private static string GetSeiza(int month, int day)
        {
            var target = int.Parse($"{month}{day.ToString("00")}");

            var seiza = SEIZA.FirstOrDefault(s => s.Item2 <= target && target <= s.Item3);
            return seiza.Item1;
        }

計算の都合上、山羊座は2期間に分けています。

干支と星座の算出結果を表示する

日付を選択した後、干支と星座を画面に表示するようにします。
MainActivity.csのDateSelectButton_Clickを以下のように変更します。

MainActivity.cs
        /// <summary>
        /// SELECT DATEボタン押下時の処理
        /// </summary>
        /// <param name="sender"></param>
        /// <param name="e"></param>
        private void DateSelectButton_Click(object sender, System.EventArgs e)
        {
            var frag = DatePickerFragment.NewInstance(delegate (DateTime time)
            {
                selectedDateLabel.Text = time.ToString("yyyy年MM月dd日") 
                    + $"あなたは、{GetEto(time.Year)}{GetSeiza(time.Month, time.Day)}です。";
            });
            frag.Show(FragmentManager, DatePickerFragment.TAG);
        }

実行してみる

実機でデバッグ実行します。
SELECT DATEボタンをタップして日付を選択し、OKをタップします。
算出結果.png
日付の選択、干支と星座の算出結果が表示されました。

まとめ

今回は、選択した日付の干支×星座を算出し、画面に算出結果を表示するところまでを行いました。
次回は、もう少し見栄えを良くしようと思います。
次回分はこちら⇒C#でAndroidアプリを作ってみる 5 (見栄えの調整)

  • このエントリーをはてなブックマークに追加
  • Qiitaで続きを読む

[C#]ExcelファイルからPDFファイル出力

1. はじめに

Microsoft® Visual Studio® Community 2019 の Visual C#®で、Excel ファイルから PDF ファイル出力する方法の忘備録になります。

2. 概要

Microsoft.Office.Interop.Excel の ExportAsFixedFormat メソッドを利用して、 オープンした Excel ファイルを、PDF ファイル形式で出力します。

3. 詳細

1) 参照設定

Visual Studio の [プロジェクト] メニューの [参照の追加] で [参照 マネージャー] を表示して、[COM] - [Microsoft Excel 16.0 Object Library] にチェックして [OK] を押下します。

2) サンプルコード

ExcelManager.cs
using System;
using Microsoft.Office.Interop.Excel;

    class ExcelManager: IDisposable
    {
        private Application _application = null;
        private Workbook _workbook = null;
        private bool disposedValue = false;

        /// <summary>
        /// Excelワークブックを開く
        /// </summary>
        public void Open()
        {
            // Excelアプリケーション生成
            _application  = new Application()
            {
                // 非表示
                Visible = false,
            };

            // Bookを開く
            _workbook = _application .Workbooks.Open(@"e:\book1.xlsx");
        }

        /// <summary>
        /// Excelワークブックをファイル名を指定してPDF形式で保存する
        /// </summary>
        /// <returns>true:正常終了、false:保存失敗</returns>
        public bool SaveAsPDF()
        {
            try
            {
                // 全シートを選択する
                _workbook.Worksheets.Select();

                // ファイル名を指定してPDF形式で保存する
                // ExportAsFixedFormatメソッド: ブックを PDF または XPS 形式に発行する
                //  Type    : タイプ   : XlFixedFormatType xlTypePDF=PDF, xlTypeXPS=XPS
                //  Filename: 出力ファイル名
                //  Quality : 種t力品質: XlFixedFormatQuality xlQualityStandard=標準品質, xlqualityminimum=最小限の品質

                _workbook.ExportAsFixedFormat(
                    Type: XlFixedFormatType.xlTypePDF,
                    Filename: @"e:\book1.pdf",
                    Quality: XlFixedFormatQuality.xlQualityStandard);
            }
            catch
            {
                return false;
            }

            return true;
        }

        protected virtual void Dispose(bool disposing)
        {
            if (!disposedValue)
            {
                if (disposing)
                {
                    // TODO: Managed Objectの破棄
                }

                if (_workbook != null)
                {
                    _workbook.Close();
                    Marshal.ReleaseComObject(_workbook);
                    _workbook = null;
                }

                if (_application != null)
                {
                    _application.Quit();
                    Marshal.ReleaseComObject(_application);
                    _application = null;
                }

                disposedValue = true;
            }
        }

        ~ExcelManager()
        {
            Dispose(false);
        }

        public void Dispose()
        {
            Dispose(true);
            GC.SuppressFinalize(this);
        }
test.cs
using System;
using System.Windows.Forms;

    public partial class test: Form
    {
        public test()
        {
            InitializeComponent();
        }

        private void Btn1_Click(object sender, EventArgs e)
        {
            // for Microsoft.Office.Interop.Excel

            using (var excel = new ExcelManager())
            {
                excel.Open();
                if (excel.SaveAsPDF() == false)
                    MessageBox.Show("ファイルが既に開かれています。\n閉じてから、再試行してください。",
                        "エラー",
                        MessageBoxButtons.OK,
                        MessageBoxIcon.Error);
            }
        }
    }

参考

【C#】ExcelファイルをPDF化する
ExportAsFixedFormat メソッド (Excel)

  • このエントリーをはてなブックマークに追加
  • Qiitaで続きを読む

【Xamarin】C#でAndroidアプリを作ってみる 3 (画面作成1)

概要

前回:C#でAndroidアプリを作ってみる 2 (デバッグ実行)
今回は、ZodiacSignApp(干支×星座算出アプリ)の画面を作ります。

レイアウトの作成

ZodiacSignApp\Resources\layout\activity_main.xmlを開きます。
初期状態では、RelativeLayoutタグしかありません。

SELECT DATEボタンと選択日付の表示ラベルを追加します。
コードは以下の通りです。

activity_main.xml
<RelativeLayout xmlns:android="http://schemas.android.com/apk/res/android"
    xmlns:app="http://schemas.android.com/apk/res-auto"
    xmlns:tools="http://schemas.android.com/tools"
    android:layout_width="match_parent"
    android:layout_height="match_parent">

    <Button
        android:text="SELECT DATE"
        android:layout_width="wrap_content"
        android:layout_height="wrap_content"
        android:minWidth="25px"
        android:minHeight="25px"
        android:id="@+id/date_select_button" />

    <TextView
        android:id="@+id/selected_date_label"
        android:layout_height="match_parent"
        android:layout_width="wrap_content" 
        android:layout_below="@id/date_select_button"/>

</RelativeLayout>

日付選択ダイアログを表示する

SELECT DATEボタンを押下すると、日付選択ダイアログが表示されるイメージです。
Android 日付の選択を参考に作ります。

DatePickerFragmentクラスを作ります。
コードは以下の通りです。

DatePickerFragment.cs
using Android.App;
using Android.OS;
using Android.Widget;
using System;

namespace ZodiacSignApp
{
    /// <summary>
    /// 日付選択ダイアログフラグメント
    /// </summary>
    public class DatePickerFragment : DialogFragment, DatePickerDialog.IOnDateSetListener
    {
        // タグ
        public static readonly string TAG = "X:" + typeof(DatePickerFragment).Name.ToUpper();

        // 日付選択処理のデリゲート
        Action<DateTime> dateSelectedHandler = delegate { };

        /// <summary>
        /// インスタンス生成
        /// </summary>
        /// <param name="onDateSelected"></param>
        /// <returns></returns>
        public static DatePickerFragment NewInstance(Action<DateTime> onDateSelected)
        {
            var frag = new DatePickerFragment();
            frag.dateSelectedHandler = onDateSelected;
            return frag;
        }

        /// <summary>
        /// ダイアログ生成
        /// </summary>
        /// <param name="savedInstanceState"></param>
        /// <returns></returns>
        public override Dialog OnCreateDialog(Bundle savedInstanceState)
        {
            var now = DateTime.Now;
            var dialog = new DatePickerDialog(Activity, this, now.Year, now.Month - 1, now.Day);
            return dialog;
        }

        /// <summary>
        /// IOnDateSetListener.OnDateSetの実装
        /// </summary>
        /// <param name="view"></param>
        /// <param name="year"></param>
        /// <param name="month"></param>
        /// <param name="dayOfMonth"></param>
        public void OnDateSet(DatePicker view, int year, int month, int dayOfMonth)
        {
            var selectedDate = new DateTime(year, month + 1, dayOfMonth);
            dateSelectedHandler(selectedDate);
        }
    }
}

SELECT DATEボタン押下で日付選択ダイアログを表示するようにします。
コードは以下の通りです。

MainActivity.cs
using Android.App;
using Android.OS;
using Android.Support.V7.App;
using Android.Runtime;
using Android.Widget;
using System;

namespace ZodiacSignApp
{
    /// <summary>
    /// メインアクティビティ
    /// </summary>
    [Activity(Label = "@string/app_name", Theme = "@style/AppTheme", MainLauncher = true)]
    public class MainActivity : AppCompatActivity
    {
        // SELECT DATEボタン
        Button dateSelectButton;

        // 選択日付の表示ラベル
        TextView selectedDateLabel;

        protected override void OnCreate(Bundle savedInstanceState)
        {
            base.OnCreate(savedInstanceState);
            Xamarin.Essentials.Platform.Init(this, savedInstanceState);
            // Set our view from the "main" layout resource
            SetContentView(Resource.Layout.activity_main);

            selectedDateLabel = FindViewById<TextView>(Resource.Id.selected_date_label);
            dateSelectButton = FindViewById<Button>(Resource.Id.date_select_button);
            dateSelectButton.Click += DateSelectButton_Click;
        }

        /// <summary>
        /// SELECT DATEボタン押下時の処理
        /// </summary>
        /// <param name="sender"></param>
        /// <param name="e"></param>
        private void DateSelectButton_Click(object sender, System.EventArgs e)
        {
            var frag = DatePickerFragment.NewInstance(delegate (DateTime time)
            {
                selectedDateLabel.Text = time.ToString("yyyy年MM月dd日");
            });
            frag.Show(FragmentManager, DatePickerFragment.TAG);
        }

        public override void OnRequestPermissionsResult(int requestCode, string[] permissions, [GeneratedEnum] Android.Content.PM.Permission[] grantResults)
        {
            Xamarin.Essentials.Platform.OnRequestPermissionsResult(requestCode, permissions, grantResults);

            base.OnRequestPermissionsResult(requestCode, permissions, grantResults);
        }
    }
}

実行してみる

実機でデバッグ実行します。
アプリ開始.png
SELECT DATEボタンをタップします。
日付選択.png
日付を選択して、OKをタップします。
選択日付表示.png
日付が選択できました。

まとめ

今回は、レイアウトを作成し、日付選択ダイアログから日付を選択するところまでを行いました。
次回は、選択した日付の干支×星座算出を行います。
次回分はこちら⇒C#でAndroidアプリを作ってみる 4 (画面作成2)

  • このエントリーをはてなブックマークに追加
  • Qiitaで続きを読む

C# 整数型

自分用です

型名 記憶領域サイズ 扱える値の範囲
byte 1バイト 0 ~ 255
sbyte 1バイト -128 ~ 127
short 2バイト -32,768 ~ 32,767
ushort 2バイト 0 ~ 65,535
int 4バイト -2,147,483,648 ~ 2,147,483,647
uint 4バイト 0 ~ 4,294,967,295
long 8バイト -9,223,372,036,854,775,808 ~ 9,223,372,036,854,775,807
ulong 8バイト 0 ~ 18,446,744,073,709,551,615
  • このエントリーをはてなブックマークに追加
  • Qiitaで続きを読む

C# (LINQ) で Python (list, numpy) っぽくリスト操作

はじめに

Python のリストや numpy ndarray の基本操作に対応する処理を、C# でどう書けばよいか?
毎回ウェブをあちこち探すのはとても時間がかかります。

普段 Python を使っていてたまに C# で書くとき、すぐに対応が見つかるように、一通りまとめておきます。

バージョン

Python 3、C# 6.0 (.NET Framework 4.6) を前提にします。.NET Framework 3.5 でも一部を除き対応していると思われます。

配列とリスト

  • Python は標準リストと numpy.ndarray を扱います。
  • C# はリストを主に扱います。

C# の配列とリストはどちらも IEnumerable を実装しているので、LINQ の説明の多くは配列でも使えます。

(Python 標準ライブラリの array.array はあまり使われないと思いますので省略します。)

サンプルコードでの配列名とリスト名

オブジェクト名に以下の文字を入れておきます。
宣言や宣言時の型は、誤解がない範囲で省略します。

  • Python
    • 標準リスト: lst
    • 配列: arr
  • C#
    • リスト: list
    • 配列: array
Python
# 以下が import されていると仮定します
import numpy as np  # for numpy.ndarray

lst = [1, 2, 3]  # 標準リスト
arr = np.array([1, 2, 3])  # numpy.ndarray
C#
// 以下が using されていると仮定します
using System;
using System.Collections.Generic;  // for List
using System.Linq;

List<int> list = new List<int> {1, 2, 3};  // リスト (Collections.Generic)
int[] array = new int[] {1, 2, 3};  // 配列

おことわり

この記事は、数値計算や機械学習の置き換え、パフォーマンスを目的とはしていません。あくまで、Python のリストや numpy.ndarray の基本操作に類似した操作を C# で書く場合の参考です。

本来の numpy 的用途(速度重視)なら、BLAS や MKL など専用演算ライブラリがベースにあるモジュールやパッケージを使った方がもちろん高速なはず。たとえば Math.NET Numerics (MKL版) とか。

Python 基本操作

range

Python
# range(stop) / range(start, stop[, step])
range(5)  # 0, 1, 2, 3, 4  (= range(0, 5))
range(1, 5)  # 1, 2, 3, 4
# 1つ飛ばし
range(3, 10, 2)  # 3, 5, 7, 9 (3-9の奇数)

for val in range(1, 5):
    print(val)
C#
// IEnumerable<int> Range (int start, int count);
Enumerable.Range(0, 5)  // 0, 1, 2, 3, 4
Enumerable.Range(1, 4)  // 1, 2, 3, 4 (第2引数は要素数)
// 1つ飛ばし
Enumerable.Range(0, 4).Select(i => i * 2 + 3)  // 3, 5, 7, 9 (*1)
Enumerable.Range(3, 7).Where(i => i % 2 == 1)  // 3, 5, 7, 9 (*2)

foreach (var val in Enumerable.Range(1, 4))
{
  Console.WriteLine(val);
}

Python3 の range (Python2 だと xrange)と C# の Enumerable.Range はどちらも遅延評価されます。

range/Range もそうですが、Python では (開始値、終了値+1) とするのに対し、C# では (開始値、要素数) とするメソッドが多いようです。

Python の range に合わせる

Python の range(start, stop, step) に合わせた実装を考えてみます。

C#でrange(python)に近いもの
// (*1) を拡張してみる(負の数や step < 0 にも対応.step = 0 のエラー処理省略)
Enumerable.Range(0, (int)Math.Ceiling((stop-start)/(double)step))
    .Select(i => i * step + start)

// (*2) を拡張してみる(これだと負の数には対応できないし効率悪そう)
// Enumerable.Range(start, stop-start).Where(i => i % step == start % step)

// いっそのこと for ループで静的メソッドを定義してしまう方が分かりやすいかも
public static IEnumerable<int> MyRange(int start, int stop, int step)
{
    if (step > 0) for (int i = start; i < stop; i += step) yield return i;
    else if (step < 0) for (int i = start; i > stop; i += step) yield return i;
    else yield break;
}

遅延評価せずリストや配列にする

Python
lst = list(range(0, 5))
arr = np.array(range(0, 5))
C#
list = Enumerable.Range(0, 5).ToList();
array = Enumerable.Range(0, 5).ToArray();

numpy.arange

Python
np.arange(0, 1, 0.1)  # 0, 0.1, ..., 0.9
C#
Enumerable.Range(0, 10).Select(i => i * 0.1);  // 0, 0.1,..., 0.9

一般には、先の「(*1)を拡張してみる」の実装で、小数の場合でも行けるのではと思います。

一方、先の MyRange を double 版の実装にして 0.1 ずつインクリメントなどしてしまうと、丸め誤差で大小比較の判定がうまくいかないです。decimal 版なら大丈夫でしょう。

内包表現や各要素に対する演算

Python
lst2 = [x * x for x in lst1]  # もしくは [x ** 2 for x in lst1]
arr2 = arr1 * arr1  # もしくは arr1 ** 2 (powerは遅いはず)
C#
list2 = list1.Select(x => x * x).ToList();

内包表現 (list comprehension) を使っていたところは、LINQ の Select で何とかなることが多いです。あとは、文脈をよく考えて、遅延評価をするかしないか判断が必要になります。
遅延評価した方がよいなら ToList() せず IEnumerable のままにします。

例:要素の総和で正規化

要素の総和が1になるように正規化する方法をまとめておきます。確率計算でもよく使います。

Python
lst2 = [x / sum(lst1) for x in lst1]
arr2 = arr1 / sum(arr1)  # もしくは arr1 / arr1.sum()
C#
list2 = list1.Select(x => x / list1.Sum()).ToList();

map, filter

Python では、イテレータが欲しい、遅延評価したい、という場合はリスト内包表現ではなく mapfilter を使えますが、これは LINQ だとそのまま SelectWhere に対応しそうです。

Python
for x in map(lambda x: x * x, lst):  # 各要素の二乗を列挙して表示
    print(x)
for x in filter(lambda x: x < 4, lst):  # 4未満の要素を表示
    print(x)

# 組み合わせ:二乗した要素が10未満なら二乗した値を表示
for x in filter(lambda x : x < 10, map(lambda x: x * x, lst)):
    print(x)
C#
foreach(var x in list.Select(x => x * x))   // 各要素の二乗を列挙して表示
    Console.WriteLine(x);
foreach(var x in list.Where(x => x < 4))  // 4未満の要素を表示
    Console.WriteLine(x);

// 組み合わせ:二乗した要素が10未満なら二乗した値を表示
foreach(var x in list.Select(x => x * x).Where(x => x < 10))
    Console.WriteLine(x);

filtermap が組み合わさってくると、LINQ のようにメソッドチェーンで書く方が分かりやすいですね。

enumerate

リストや配列の値だけでなくそのインデックスも欲しい場合、Python では enumerate ですが、C# では Select を使えます。

Python
lst1 = ['foo', 'bar']
lst2 = ['[{0}] {1}'.format(i, s) for i, s in enumerate(lst1)]
C#
var list1 = new List<string> {"foo", "bar"};
var list2 = list1.Select((s, i) => string.Format("[{0}] {1}", i, s)).ToList()

LINQ の Select は、value, index の順序が Python の enumerate と逆なので注意が必要です。

zip および要素ごとの演算

Pythonで2つのリストをzip
lst3 = [x - y for x, y in zip(lst1, lst2)]  # 要素ごとの引き算
sum([x * y for x, y in zip(lst1, lst2)])  # 内積計算
NumPyで要素ごとの演算
arr3 = arr1 - arr2  # 要素ごとの引き算
arr1.dot(arr2) # もしくは np.dot(arr1, arr2)  # 内積計算
C#
list3 = list1.Zip(list2, (x, y) => x - y).ToList();  // 要素ごとの引き算
list1.Zip(list2, (x, y) => x * y).Sum();  // 内積計算
// もしくは
list3 = Enumerable.Zip(list1, list2, (x, y) => x - y).ToList();  // 要素ごとの引き算
Enumerable.Zip(list1, list2, (x, y) => x * y).Sum();  // 内積計算

3つ以上のリスト(配列)を組み合わせる

Python
lst4 = [x + y + z for x, y, z in zip(lst1, lst2, lst3)]  # 要素ごとの足し算
arr4 = arr1 + arr2 + arr3  # 要素ごとの足し算
C#で3つのリストをZip
list4 = list1.Zip(list2, (x, y) => x + y)
    .Zip(list3, (zip1, z) => zip1 + z).ToList(); // 要素ごとの足し算

LINQ の Zip は3つ以上のリストをとれないので、Zip を繰り返して使うことになるようです。

一段目の Zip でペアをそのまま次段へ匿名型で渡せば、二段目の Zip でまとめることもできます。

C#で3つのリストをZip(匿名型でつなげる)
list4 = list1.Zip(list2, (x, y) => new {x, y})
    .Zip(list3, (zip1, z) => zip1.x + zip1.y + z).ToList(); // 要素ごとの足し算

初期化

0 で初期化

Python
lst = [0] * 10  # 1次元ならこれでOK (入れ子など参照が絡む場合は落とし穴)
lst = [0 for _ in range(10)]  # これもOK (入れ子など参照が絡んでくるならこちら)
arr = np.zeros(10)  # 0 で初期化された要素10の ndarray
C#
int[] array = new int[10];  // 0 で初期化された要素10の配列
List<int> list = new int[10].ToList();
List<int> list = new List<int> (new int[10]);  // コンストラクタの引数に配列を渡す

同じ値で埋めて初期化

Python
# False で埋めた配列
lst = [False] * 10  # 1次元ならこれでOK (入れ子など参照が絡む場合は落とし穴)
lst = [False for _ in range(10)]  # 入れ子リストなど参照が絡んでくるならこちら
arr = np.full(10, False)  # False で初期化された要素10の ndarray
C#
List<bool> list = Enumerable.Repeat(false, 10).ToList();  // こちら落とし穴あり(*1)
List<bool> list = Enumerable.Range(0, 10).Select(_ => false).ToList();
List<bool> list = new bool[10].Select(_ => false).ToList();
// いずれも ToList() を ToArray() にすれば配列に

(*1) リストを入れ子で作る場合など、参照型で ’Repeat’ してしまうと、ひとつ変えるとすべて変わります。この落とし穴は Python の [値] * 要素数 と同じです。
https://qiita.com/yosizo@github/items/1adcff1fc974cde5256a

連番の値で初期化

「遅延評価せずリストや配列にする」の通り、RangeToList()ToArray() で連番の値を持ったリストや配列を作ることができます。Python で range から listnp.array を作るのに対応します。

乱数で初期化

整数乱数で初期化

Python
import random  # 標準ライブラリ
# randint(a,b)は randrange(a,b+1)のエイリアス
lst = [random.randrange(0, 10) for _ in range(5)]  # 0以上9以下で5個
lst = [random.randint(0, 9) for _ in range(5)]  # 0以上9以下で5個
#  lst = random.sample(range(10), k=5))  # 0以上9以下で5個(重複無しなので上とは異なる)

arr = np.random.randint(0, 10, 5)  # 0以上9以下で5個

numpy の random.randint は、標準ライブラリの random.randint とは異なり(むしろ randrange と同じで)指定値未満の乱数を返すので注意!

C#
System.Random rg = new System.Random();  // random number generator (usingしておいてもよい)
int[] rndarray = Enumerable.Range(0, 5).Select(_ => rg.Next(0, 10)).ToArray();  // 0以上9以下で5個
List<int> rndlist = Enumerable.Range(0, 5).Select(_ => rg.Next(0, 10)).ToList();  // 0以上9以下で5個

Next()の第一引数(最小値)は省略すると0以上となります。

実数乱数で初期化

Python
import random  # 標準ライブラリ
lst = [random.random() for _ in range(5)]  # 0以上1未満で5個

arr = np.random.rand(5)  # 0以上1未満で5個
C#
double[] array = Enumerable.Range(0, 5).Select(_ => rng.NextDouble()).ToArray();  // 0以上1未満で5個
List<double> list = Enumerable.Range(0, 5).Select(_ => rng.NextDouble()).ToList();  // 0以上1未満で5個

乱数生成アルゴリズム自体を変える方法はこちらで解説されています。

値をコピーして初期化

Python
lst1 = [3, 1, 4]  # コピー元を適当に作っておく
lst2 = list(lst1)  # lst1, lst2 のオブジェクトIDは異なる
lst2 = lst1[:]  # これも可 (lst1, lst2 のオブジェクトIDは異なる)
# lst2 = lst1  # lst1, lst2 のオブジェクトIDは一致 (コピーではない)
import copy
lst2 = lst1.copy()  # これも可 (lst1, lst2 のオブジェクトIDは異なる)

arr1 = np.array([3, 1, 4])  # コピー元を適当に作っておく
arr2 = np.copy(arr1)  # lst1, lst2 のオブジェクトIDは異なる
arr2 = arr1.copy()  # これも可 (lst1, lst2 のオブジェクトIDは異なる)
# arr2 = arr1  # arr1, arr2 のオブジェクトIDは一致 (コピーではない)

C#
var list2 = new List<int>(list1);  // コピーコンストラクタで初期化
var list2 = list1.ToList();  // これも可

var array2 = new int[array1.Length];  // あらかじめ領域確保
array1.CopyTo(array2, 0);  // 第二引数はコピー先の開始 index
Array.Copy(array1, array2, array1.Length);  // これも可

var array2 = array1.ToArray();  // これも可
var array2 = array1.Clone() as int [];  // これも可 (キャスト必要)

要素へのアクセスやインデックス取得

スライス (Slice)

Python(numpy)
array[0:5]  # 0-4番目の5要素
array[5:]  # 0-4の5要素を飛ばして 5-
array[3:5]  # 0-2を飛ばして 3-4 の2要素
C#
array.Take(5)  // 0-4番目の5要素
array.Skip(5)  // 0-4の5要素を飛ばして 5-
array.Skip(3).Take(2)  // 0-2を飛ばして 3-4 の2要素

list.GetRange(3, 2)  // Listならこれも可

条件を満たす要素のインデックスを取得

Python(numpy)
idx_lst = np.where(lst == val)
C#
var indexList = list.Select((x, i) => new { x, i })
    .Where(xi => xi.x == val).Select(xi => xi.i);

最小(最大)の値を取る要素のインデックスを取得

Python(numpy)
arr = np.array([2, 1, 8, 4, 0, 8])  # 空でないとする

idx = np.argmin(arr)  # 最小値をとる最初の要素(0)のインデックス (4)
idx = np.argmax(arr)  # 最大値をとる最初の要素(8)のインデックス (2)
C#
var list = new List<int> { 2, 1, 8, 4, 0, 8 };
// var list = new List<int>();  // 空のケースも考慮するならコメントアウトした方

var argmin = list.Select((x, i) => new { x, i })
    .Aggregate((min, xi) => xi.x < min.x ? xi : min).i;  // 4
//    .Aggregate(new { x = int.MaxValue, i = -1 }, (min, xi) => xi.x < min.x ? xi : min).i;  // 4

var argmax = list.Select((x, i) => new { x, i })
    .Aggregate((max, xi) => xi.x > max.x ? xi : max).i;  // 2
//    .Aggregate(new { x = int.MinValue, i = -1 }, (max, xi) => xi.x > max.x ? xi : max).i;  // 2

空のリストにも対応するなら Aggregate の第一引数にシードを与えます。

「特定のキーで Min()Max() すると、そのキーの最小(最大)値だけが戻ってきてしまうが,要素のインデックスもしくは(キーを含む)オブジェクトそのものを取得したい」という質問は昔から StackOverflow でも何度も出ているようです。(Qiita でも。)

別途実装せず LINQ でシンプルに書くには、例えば以下のやり方があります。

  1. Aggregate を使う(上のコード) -> O(n) なのでよいが可読性やや難あり
  2. OrderByOrderByDescending して FirstTake -> ソートで O(n log(n)) になる
  3. MinMax で取得した最小(最大)値で Where -> 2回走査してしまう

n が小さければ、可読性重視で 2, 3 もありかも。
再利用性も考え、素直にループ(イテレータ)で実装しておくか、すでにある実装を使うのもよさそう(MoreLINQMinByMaxBy など)。

リスト操作

よく使うリスト操作の対応です。

Python(標準リスト)
lst.append(val)  # 末尾へ値を追加
lst.extend(lst2)  # 末尾へ別のリストを追加
lst.insert(idx, val)  # idx へ値を挿入
lst[idx:idx] = lst2  # idx位置に lst2の要素すべてを挿入

lst.clear()  # 要素をすべて削除
lst.remove(val)  # 最初に見つかった val の値をもつ要素削除
del lst[idx:idx+len]  # idx位置から len個の要素を削除
del lst[-1]  # 末尾を削除
C#
list.Add(val);  // 末尾へ値を追加
list.AddRange(list2);  // 末尾へ別のリストを追加
list.Insert(idx, val);  // idx へ値を挿入
list.InsertRange(idx, list2);  // idx位置に list2の要素すべてを挿入

list.Clear();  // 要素をすべて削除
list.Remove(val);  // 最初に見つかった val の値をもつ要素削除
list.RemoveRange(idx, len);  // idx位置から len個の要素を削除
list.RemoveAt(list.Count - 1);  // 末尾を削除

// その他の操作
list.RemoveAll(v => v == val);  // ラムダ式で削除する要素の条件を指定 (左例は val 値をもつ要素全て削除)

文字列操作

リストが絡む文字列操作に限定して取り上げます。

split, join

Python
str1 = 'pen-pineapple-apple-pen'
str1_list = str1.split('-')

str2_list = ['pen', 'pineapple', 'apple', 'pen']  # str1_list
str2 = '-'.join(str2_list)  # str1
C#
string str1 = "pen-pineapple-apple-pen";
string[] str1Array = str1.Split('-');  // Split は配列を返す
List<string> str1List = str1Array.ToList();  // リストが必要なら変換

List<string> str2List = new List<string> {"pen", "pineapple", "apple", "pen"};
string str2 = string.Join("-", str2List);  // 可変長引数で文字列を渡すことも可能
// string str2 = str2List.Aggregate((x, y) => x + "-" + y);  // 空リストだとInvalidOperation
// string str2 = str2List.Aggregate(string.Empty, (x, y) => x + "-" + y).TrimStart('-');

しばらく別の言語で書いていると、Python の join は書き方に迷います。

C# の Split では、セパレータに char を渡すときは単純ですが、文字列を渡す場合は第二引数のオプション指定(StringSplitOptions.None など)が必要です。
Join は基本文字列を渡します・・・ややこしいですね。

string.Join の代わりに Aggregate を使うとメソッドチェーンで書けますが、こちらの記事で議論されているようにいくつか問題があります。
まず、空のリストが渡されると例外が投げられます。初期値として空文字を渡すと例外を回避できますが、今度は先頭にもセパレータがついてしまうため、これを削除(上の例では TrimStart)する必要があります。

StackOverflow では以下のような書き方も紹介されていました。パフォーマンスも string.Join 並みによいということです。

C#/LINQのAggregateで文字列を結合
string strJoin = strList.Aggregate(new StringBuilder(),
    (x, y) => x.Append(x.Length==0 ? "" : "-").Append(y)).ToString();

ただ、いくつかの記事やスレッドを追った感じでは、あえて Aggregate にせず、素直に string.Join する方がよい、という結論が多いようです。

print とフォーマット指定

Python
lst = [1/2, 3/4, 5/6]
print(lst)  # [0.5, 0.75, 0.8333333333333334]
print("[" + ", ".join("%.2f" % x for x in lst) + "]")  # [0.50, 0.75, 0.83]

arr = np.array([1/2, 3/4, 5/6])
print(arr)  # [0.5        0.75       0.83333333]
print(np.array_str(arr, precision=2))  # [0.5  0.75 0.83]
print(np.array2string(arr, precision=2, floatmode='fixed'))  # [0.50  0.75 0.83]
# array2string はオプション豊富.小数点以下の桁数を固定して0埋めできる
# array_str の中身は array2string とのこと

# numpy は以下でも可(一時的に使うなら元の設定に戻しておく)
orig_printopt = np.get_printoptions()  # 現在の設定を辞書へ
np.set_printoptions(precision=2, floatmode='fixed')
print(arr)  # [0.50  0.75 0.83]
np.set_printoptions(**orig_printopt)  # 設定を戻す(辞書を展開して引数へ)
C#
var list_print = new List<double> {1.0/2, 3.0/4, 5.0/6};
Console.WriteLine("[" + string.Join(", ", list) + "]");  // [0.5, 0.75, 0.833333333333333]
Console.WriteLine("[" + string.Join(", ", list.Select(x => x.ToString("0.00"))) + "]");  // [0.50, 0.75, 0.83]

ソート

昇順・降順ソートと安定性

Python
sorted_lst = sort(lst)  # 昇順ソートされたリストが返る
lst.sorted()  # lstそのものが昇順ソートされる (in-place)

sorted_data = np.sort(arr)  # 昇順ソート
sorted_data = np.sort(arr)[::-1]  # 降順ソート
C#
Array.Sort(array);  // arrayそのものが昇順ソートされる (inplace)
Array.Reverse(array);  // 昇順ソート後にひっくり返して逆順へ

list.Sort();  // listそのものが昇順ソートされる
list.Reverse();  // 昇順ソート後にひっくり返して逆順へ

// ラムダ式を使うと逆順ソートも一回で
Array.Sort(array, (x, y) => y - x);  // arrayそのものが逆順ソートされる
list.Sort((x, y) => y - x);  // listそのものが逆順ソートされる

// LINQを使ってもソートできる(特に複数のキーがある場合は便利.リストでも利用可)
sorted_array = array.OrderBy(x => x);  // 昇順ソート(戻り値で受け取る)
sorted_array = array.OrderByDescending(x => x);  // 降順ソート(戻り値で受け取る)

List の内部は配列なのでソートのアルゴリズムは同じとか。
ソートアルゴリズムの安定性について:Reverse() はちょっと強引ですが、もともと Array.Sort() も安定ソートではないので、同じ値のデータの順序を気にしない場合はありでしょう。
一方で、LINQ の OrderBy() は安定ソートとのこと(Stack Overflow より)。安定なソートが欲しい場合は LINQ が手っ取り早いかも。

ちなみに Python では list のソートは安定で、numpy でも 'stable' をオプションで渡すと安定性が保証されます。

ソートされたデータの添え字

Python
idx = np.argsort(arr)
C#(array1とインデックスのソート結果をarray2とidxへ)
// Array.Sort で
int[] array2 = new int[array1.Length];  // ソート結果を別配列にするための準備
array1.CopyTo(array2, 0);  // array1 を array2 にコピー
int[] idx = Enumerable.Range(0, array2.Length).ToArray();  // 連番の配列 0,1,...
Array.Sort(array2, idx);  // 別配列も一緒に並べ替えるオーバーロードがあるのでこれを使う
for(var i=0; i < array2.Length; i++)  // ついでに表示
{
    Console.WriteLine($"key: {array2[i]}, idx: {idx[i]}");
}

// LINQ で
var sortedPair = array1
    .Select((x, i) => new KeyValuePair<int, int>(x, i))
    .OrderBy(x => x.Key);
var array2 = sortedPair.Select(x => x.Key).ToList();
var idx = sortedPair.Select(x => x.Value).ToList();

// 分かりやすい名前でタプル(ペア)を作りそのまま使ってしまうのもあり
var sortedPair = array1
    .Select((x, i) => (Key: x, Index: i))  // C# 7 (.NET Framework 4.7 など)
    .OrderBy(x => x.Key)
    .ToList();
sortedPair.ForEach(x => {  // ついでに表示
    Console.WriteLine($"key: {x.Key}, idx: {x.Index}");
});

この Stack Overflow の記事を参考にしました。

二つの系列をまとめてソート

C#
var list1 = new List<int> { 3, 4, 1, 2 };
var list2 = new List<int> { 6, 7, 8, 9 };
var zipped = list1.Zip(list2, (x1, x2) => new {key1 = x1, key2 = x2});
var sorted = zipped.OrderBy(x => x.key1).Select(x => x.key2);  // 8, 9, 6, 7

基本的な統計量や数学関数

基本的な統計量

Python
np.mean()
np.std()
np.max()
np.min()
np.argmax()
C#
double mean = data.Average();
double varP = data.Select(x => (x - mean) * (x - mean)).Sum() / data.Length;
double stdevP = Math.Sqrt(var);

そろそろ Math.NET などパッケージを入れたほうがよいかもしれません。

bool型の統計量

Python
lst = [True, False, True]
sum(lst)  # 2
any(lst)  # True
all(lst)  # False
C#
var list = new bool[] { true, false, true };
list.Count()  // Sum() ではない
list.Any(v => v)  // true
list.All(v => v)  // false

数学関数

Python
np.round()
C#
Math.Round()

多次元配列

以下書きかけです。リストのリストを矩形行列としてみた操作(つまり内側のリストはすべて同じ長さと仮定)。

初期化

Python
mat = np.zeros((NROWS, NCOLS))
C#
List<List<double>> mat = Enumerable.Range(0, NROWS).Select(_ => new double[NCOLS].ToList()).ToList();

Repeat ではなくそれぞれ実体化していく。

行や列をコピー

Python
arr = mat[i,:].copy()
arr = mat[:,j].copy()
arr = mat.flatten()
C#
var list = mat[i];
var list = mat.Select(r => r[j]).ToList();
var list = mat.SelectMany(r => r).ToList();  // flatten

列ごとの平均

Python
mat.mean(0)
C#
var ret = mat.SelectMany(r => r.Select((val, j) => new {val, j}))
     .GroupBy(x => x.j, (key, y) => {return y.Average(z => z.val);});

いったん {val, j} を要素とする x へ平坦化したあとに、インデックス j でグルーピングして y とし、それぞれのグループで平均を計算している。

参考: https://teratail.com/questions/76764

その他(未テスト)

DeepCopy

転置

  • このエントリーをはてなブックマークに追加
  • Qiitaで続きを読む

Taskじゃない非同期をTaskに変換したい(TaskCompletionSource)

はじめに

Task(UniTask)は便利です。
特にこの昨今awaitで結果を待てるので手続き処理っぽく書きつつ非同期処理が書けます。

でも、 Taskじゃない非同期処理はどうしましょう。

例えばボタンのクリック。 ボタンのクリックはユーザーが気が向いたときにするものなので非同期です。

PlayFabやNCMBといったmBaasのAPI呼び出しなどは通信中画面が止まっては困るのでコールバックで結果(もしくはエラー)を受け取るようになっています。 これもまた非同期です。

こういった「非同期だけれど、Taskではない」

をTaskに変換してくれる便利な奴がいます。 それが TaskCompletionSource(UniTaskCompletionSource)です。

※この記事では基本Task/TaskCompletionSourceを使いますが、適宜UniTask/UniTaskCompletionSourceに読みかえてください

基本形

public Task<int> GetIntAsync(){
    var tcs = new TaskCompletionSource<int>();



    return tcs.Task;
}

intを返すTask Task<int> に対応するのは TaskCompletionSource<int> ですし
floatを返すTask Task<float> に対応するのは TaskCompletionSource<float> です。

この返却したいTaskに対応した TaskCompletionSource を new して、 Task をreturn するのが基本形です。

しかし、このメソッドのTaskはいつまでたっても結果を返しません。 それはそう。 誰も結果を返してないからです。

結果のセット

結果を返すにはこのTaskCompletionSourceのインスタンスに TrySetResult を呼ぶ必要があります。
(なお、キャンセルする場合は TrySetCanceled()を呼ぶ必要がありますが、それはまたの機会に・・・)

public Task<int> GetIntAsync(){
    var tcs = new TaskCompletionSource<int>();

    tcs.TrySetResult(32768);//結果のセット

    return tcs.Task;
}

public async Task RunAsync(){
    var num = await GetIntAsync(); 
    Debug.Log(i); //→ 32768 
}

これで、このメソッドはTaskの結果として32768を返すようになります。
さて。これは非同期でしょうか。違いますね。 これでは return Task.FromResult(32768); と同じです。

しかし、ここで一つとても重要なことがあります。それはこの TrySetResultは非同期で呼んでも構わないということです。

と言われても良くわからない方もいるでしょう。
例えば。 最初に例で出した ボタンのクリック を絡めてみます。

例1 指定したボタンをクリックしたらintを返却するTask

まず、基本形を作ります

public Task<int> ButtonClickAsync(){
    var tcs = new TaskCompletionSource<int>();

    return tcs.Task;
}

「指定したボタンを」とあるので、引数でボタンを受け取ります

public Task<int> ButtonClickAsync(Button btn){
    var tcs = new TaskCompletionSource<int>();

    return tcs.Task;
}

ボタンがクリックされたら、結果を返却するようにします。 結果を返却するには TaskCompletionSourceTrySetResult を呼べば良いのでした。

public Task<int> ButtonClickAsync(Button btn)
{
    var tcs = new TaskCompletionSource<int>();

    btn.onClick.AddListener(() => tcs.TrySetResult(65536));//ボタンクリックイベントのコールバックで結果セット

    return tcs.Task;
}

完成です。これで、ボタンをクリックするとこの tcs.Task は 65536 を返却して終了するようになります。
簡単ですね。無事 Taskじゃない非同期処理をTask化できました

※これだとbtnのonClickイベントが登録されっぱなしじゃないか!と思った貴方。大正解です。本当はちゃんと解除してあげてください。

では、次はほんのちょっと応用です。 (今となってはObsoleteな)WWWクラスをTask化してみます

例2 指定したURLからページ情報を取得するTask

これまた、基本形を作ります。 ページ情報なので Task は Task<string> TaskCompletionSourceも TaskCompletionSource<string> にします。

public Task<string> WWWAsync(){
    var tcs = new TaskCompletionSource<string>();

    return tcs.Task;
}

「指定したURLから」とあるので、引数でurlを受け取ります

public Task<string> WWWAsync(string url){
    var tcs = new TaskCompletionSource<string>();

    return tcs.Task;
}

WWWクラスはコルーチンで結果を待つ仕組みなので、StartCoroutineで指定したurlからWWWでコンテンツを取得するようにします。

public Task<string> WWWAsync(string url){
    var tcs = new TaskCompletionSource<string>();

    StartCoroutine(WWWGet(url));

    return tcs.Task;
}

private IEnumerator WWWGet(string url)
{
    var www = new WWW(url);
    yield return www;
    //wwww.text; //TODO ここにダウンロードしたコンテンツが入っている
}

結果を返却するには TaskCompletionSourceTrySetResult を呼べば良いのですが、コルーチンの結果は(基本的には)取得できないので、WWWGetの引数に TaskCompletionSource<string> を追加して、WWWGetの中で結果を入れてもらうように修正します。

public Task<string> WWWAsync(string url)
{
    var tcs = new TaskCompletionSource<string>();

    StartCoroutine(WWWGet(url,tcs));

    return tcs.Task;
}

private IEnumerator WWWGet(string url, TaskCompletionSource<string> tcs)
{
    var www = new WWW(url);
    yield return www;
    tcs.TrySetResult(www.text);
}

完成です。 コルーチンによる非同期もTaskにすることが出来ました。

合体!

完全に蛇足ですが、上記二つの例を組み合わせると、「ボタンがクリックされるまで待ち、クリックされたら指定されたURLにアクセスしてコンテンツを取得する」Taskが作れます。

    private async Task RunAsync()
    {
        var i = await ButtonClickAsync(_btn);//指定したボタンがクリックされるのを待つTask
        Debug.Log(i);

        var content = await WWWAsync("http://yahoo.co.jp");//指定したURLのコンテンツを取得するTask
        Debug.Log(content);
    }

んー。すっきりしてますね!

最後に

今回は非常にシンプルな例を挙げましたが、使ってみるとっかかりにでもなれば幸いです。

Taskにしておくと結果が取得しやすいのはもちろんのこと、Task.WhenAnyTask.WhenAllを使って、複数あるうちのどれか一つのTaskが完了待ちや全てのTask完了待ちなど、ただの非同期処理では難しかった非同期処理同士の連携が簡単にできるようになったりするので(状況に応じて)Task化進めるのも良いと思います。

補足

  • 基本形として TaskCompletionSource をローカル変数で作りましたが、メンバ変数でも構いません。 というか、その方が便利な場合も多々あります。 ただし、TaskCompletionSourceの使いまわしはできません。 1回 SetResultしたらそれで終わりなので、また同様のTaskを呼ぶ場合は TaskCompletionSource を再インスタンス化する必要があります。
  • ボタンクリックや、wwwの待ち等は実は UniRX.Async を使っていればこんなことせずに await が可能です。 (ボタンクリック待ちのTaskは .OnClickAsync(); が用意されています)今回の内容はあくまでも TaskCompletionSource の使い方の簡単な説明なのであしからず。
  • TaskCompletionSource には、結果を返さないただのTaskがありません。 そのため別に返却する値が無い場合でも TaskCompletionSource<object> とし、 TrySetResult(null); とやることがよくあります。 が、 UniTaskCompletionSource には非Generics版が用意されているので、返却値が無い場合はそちらを使用しましょう。
  • イベント周りはUniRX使った方が適切なケースも多いです。適宜使い分けましょう。

よい。Taskライフを。

  • このエントリーをはてなブックマークに追加
  • Qiitaで続きを読む

[WPF] MVVM的にファイルを開くダイアログをコマンドとして実装する

はじめに

ここでのファイルを開くダイアログとは、Microsoftが提供しているWindowsAPICodePackCommonOpenFileDialogのことを指します。入っていない方はNugetからインストールしておいて下さい。
FormsWin32OpenFileDialogを使用したい場合(いるのかどうかわかりませんが)は適宜読み替えて下さい。
PrismなどのMVVMインフラは利用しません。

概要

WPFアプリケーションでファイルを開くダイアログを使いたい時ってありますよね。
ファイルを開くダイアログはつまるところViewなので、ViewModelでインスタンスを作ってShowDialogして…というのはNGです。具体的には次のようなコードですね。

' NG例
Public Class HogeViewModel

    ' ...

    Private Sub OpenFile()
        Dim dialog As New CommonOpenFileDialog()
        If dialog.ShowDialog() = CommonFileDialogResult.Ok Then
            Dim fileName As String = dialog.FileName
            ' ...
        End If
    End Sub

    ' ...

End Class

これじゃイカンということで、世の中にはアクションやビヘイビアを作ってコールバックを渡したり、ファイルを開くダイアログ用のサービスを作ったりするアプローチが存在します。
今回は、ViewModelで定義されたコマンドのExecuteに選択されたファイル名を渡すラッパーコマンド(?)のようなものを作って解決してみようと思います。

クラス定義

ICommandを実装した、OpenFileDialogCommandクラスを作成します。

OpenFileDialogCommand.vb
    Public Class OpenFileDialogCommand
        Inherits DependencyObject
        Implements ICommand

#Region "Dependency Property Registration"

        Public Shared ReadOnly CommandProperty As DependencyProperty =
            DependencyProperty.RegisterAttached("Command", GetType(ICommand), GetType(OpenFileDialogCommand))

#End Region

#Region "Properties"

        Public Property Command As ICommand
            Get
                Return GetValue(CommandProperty)
            End Get
            Set(value As ICommand)
                SetValue(CommandProperty, value)
            End Set
        End Property

#End Region

#Region "Methods"

        ''' <summary>
        ''' コマンドの実行可否を手動で更新します。
        ''' </summary>
        Public Sub RaiseCanExecute()
            RaiseEvent CanExecuteChanged(Me, EventArgs.Empty)
        End Sub

#End Region

#Region "ICommand"

        Public Function CanExecute(parameter As Object) As Boolean Implements ICommand.CanExecute
            If IsNothing(Me.Command) Then
                Return False
            Else
                Return Me.Command.CanExecute(parameter)
            End If
        End Function

        Public Custom Event CanExecuteChanged As EventHandler Implements ICommand.CanExecuteChanged
            AddHandler(value As EventHandler)
                AddHandler CommandManager.RequerySuggested, value
            End AddHandler
            RemoveHandler(value As EventHandler)
                RemoveHandler CommandManager.RequerySuggested, value
            End RemoveHandler
            RaiseEvent(sender As Object, e As EventArgs)
                CommandManager.InvalidateRequerySuggested()
            End RaiseEvent
        End Event

        Public Sub Execute(parameter As Object) Implements ICommand.Execute
            Dim dialog As New CommonOpenFileDialog()
            Dim res As CommonFileDialogResult = dialog.ShowDialog()
            If res <> CommonFileDialogResult.Ok Then Return

            If IsNothing(Me.Command) = False Then
                If Me.Command.CanExecute(parameter) Then Me.Command.Execute(dialog.FileName)
            End If
        End Sub

#End Region

    End Class

使い方は、Commandプロパティにメインとなる(Executeにファイル名を渡したい)コマンドをバインドするだけです。次のような感じです。

<Button Content="開く">
  <Button.Command>
    <local:OpenFileDialogCommand Command="{Binding HogeCommand}" />
  </Button.Command>
</Button>

一件落着…と言いたいところですが、このままだとバインディングエラーが吐かれます。

System.Windows.Data Error: 2 : Cannot find governing FrameworkElement or FrameworkContentElement for target element. 以下略

コードに問題はないはずなのにバインディング先が見つからんと言われます。
結論から言うと、DataContextが引き継がれていないためこのようなエラーが吐かれます。
DataContextを引き継ぐようにするには、Freezableを継承する必要があります。FreezableDependencyObjectを継承しているので継承部分だけ書き換えればOKです。

OpenFileDialogCommand.vb
    Public Class OpenFileDialogCommand
        Inherits Freezable
        Implements ICommand

        ' ...

#Region "Freezable"

        Protected Overrides Function CreateInstanceCore() As Freezable
            Return New OpenFileDialogCommand()
        End Function

#End Region

    End Class

Freezableを継承したクラスは、CreateInstanceCoreメソッドをオーバーライドする必要があります。ここでは自身のクラスのインスタンスを返せば良いです。
これでバインディングエラーはなくなるはずです。

おわりに

このままだとダイアログのプロパティを何も設定せずに表示するので使い勝手が悪いです。なので必要に応じてプロパティをXamlから設定できるようにしておくと良いでしょう。

追記

2019/10/28 DependencyObjectが抜けていたのを修正しました。

  • このエントリーをはてなブックマークに追加
  • Qiitaで続きを読む

内容

1.基本UI
2.カードデータPrefab作成
3.デッキのランダム生成
4.デッキから手札へカードドロー
5.手札がMAXならこれ以上ドローしない
6.デッキ枯渇したらこれ以上ドローしない
7.ターン交代でアクティブプレイヤー交代
8.ターン開始時ドロー
9.リスタートでオブジェクト初期化
10.ターン交代ボタン設置
11.

  • このエントリーをはてなブックマークに追加
  • Qiitaで続きを読む

Error: rzc generate exited with code 2

環境:
Visual Studio for Mac Community 8.1.3
.NET Core 2.2
ASP.NET MVC

突然以下のようなエラーになり、手詰まりになる。どこが悪いのかも教えてくれない。
/usr/local/share/dotnet/sdk/NuGetFallbackFolder/microsoft.aspnetcore.razor.design/2.2.0/build/netstandard2.0/Microsoft.AspNetCore.Razor.Design.CodeGeneration.targets(5,5): Error: rzc generate exited with code 2.

Razor View内でコメントを書いただけで上のエラーになる。

これは問題なし(No Problem)

@* a *@

更に二重にコメントアウト。これが気に入らないらしい。(Error!)

@*@* a *@*@

こちらで議論されているが解決に至っていない様子。SDKを再インストールだの・・・。
https://developercommunity.visualstudio.com/content/problem/403142/rzc-generate-exited-with-code-2.html

ちなみに、Windows版 Visual Studioはわかりやすいエラーになり、問題なし。
A space or line break was encountered after the "@" character. Only valid identifiers, keywords, comments, "(" and "{" are valid at the start of a code block and they must occur immediately following "@" with no space in between.

  • このエントリーをはてなブックマークに追加
  • Qiitaで続きを読む

.NET MVC Frameworkにおける属性チェックの基本と実装について

環境

  • Visual Studio 2019
  • .NET Framework 4.7.2
  • .NET MVC 5.2.7.0

検証の為の属性の付与と、その概要

.NET MVCでは、以下のようにSystem.ComponentModel.DataAnnotations namespaceに属する各Attributeクラスのメソッドを"[]"で囲んだ記法によって、POST時に必須等の属性チェックを行うことができる。

※例えば、[Required]は上記namespaceのRequiredAttributeクラス(ValidationAttributeクラスを継承)に属している。

HelperViewModel.cs
using System.ComponentModel.DataAnnotations;

namespace HtmlHelperTest.ViewModels
{
    public class HelperViewModel
    {
        [Required(ErrorMessage = "{0}は必須です。")]
        [StringLength(10, ErrorMessage = "{0}は{1}文字以内で入力してください。")]
        [Display(Name = "サンプルプロパティ")]
        public string TmpProperty { get; set; }
    }
}

個別の属性については説明を省くが、上記のような属性を付与した状態でPOSTすると、@Html.ValidationMessageFor()を記述した箇所に以下のようにエラーメッセージをレンダリングする。
※個別の属性についてはSystem.ComponentModel.DataAnnotations namespaceを参照

 POST前
キャプチャ.PNG

 POST後 必須チェックエラー
キャプチャ2.PNG

 POST後 桁数チェックエラー
キャプチャ3.PNG

実際に出力されるエラーメッセージは各属性の()内にあるErrorMessageで指定している。
ちなみに、[StringLength(10, ErrorMessage = "{0}は{1}文字以内で入力してください。")]のように{n}となっている個所にはそれぞれ、

  • {0}[Display(Name = "")]で指定したプロパティの表示名称
  • {1~n}:各属性の第一引数~第n引数

が表示される。

エラーメッセージの表示位置について

前節で@Html.ValidationMessageFor()に各プロパティの属性チェックのエラーメッセージがレンダリングされると書いたが、実際のViewファイルへの記述は以下のようになっている。

Index.cshtml
@model HtmlHelperTest.ViewModels.HelperViewModel

@using (@Html.BeginForm())
{
    @Html.AntiForgeryToken()
    <div>
        <div>
            @Html.LabelFor(model => model.SumpleProperty)
        </div>
        <div>
            @Html.TextBoxFor(model => model.SumpleProperty)
            @Html.ValidationMessageFor(model => model.SumpleProperty)
        </div>
        <div>
            <input type="submit" value="OK" />
        </div>
    </div>
}

他のHtmlHelper同様、@Html.ValidationMessageFor()の引数にプロパティを指定するだけだ。
このような記述によって、
キャプチャ3.PNG

このように任意の場所にエラーメッセージを表示させることができる。

しかし、場合によっては画面上部や特定の場所にエラーメッセージをまとめて表示させたい場合もある。
そのような場合には、以下のように@Html.ValidationSummary()を追加して、第一引数にfalseを指定すればよい。

Index.cshtml
@model HtmlHelperTest.ViewModels.HelperViewModel

@using (@Html.BeginForm())
{
    @Html.AntiForgeryToken()
    <!-- ↓変更箇所 コード追加↓ -->
    @Html.ValidationSummary(false)
    <!-- ↑変更箇所 コード追加↑ -->
    <div>
        <div>
            @Html.LabelFor(model => model.SumpleProperty)
        </div>
        <div>
            @Html.TextBoxFor(model => model.SumpleProperty)
            <!-- ↓変更箇所 エラー項目であることだけ分かるように"*"のみ表示↓ -->
            @Html.ValidationMessageFor(model => model.SumpleProperty, "*")
            <!-- ↑変更箇所 エラー項目であることだけ分かるように"*"のみ表示↑ -->
        </div>
        <div>
            <input type="submit" value="OK" />
        </div>
    </div>
}

こうすることで、以下のように箇条書きでエラー一覧が表示されるようになる。
また、ここではエラー項目が分かるように"*"をエラーメッセージの代わりに表示させている。
キャプチャ4.PNG
ちなみに、@Html.ValidationSummary()の第二引数に箇条書きの上に表示させるメッセージを指定できる。
※ここでは@Html.ValidationSummary(false, "エラー一覧")として実行。

Htmlタグに自動付与されるValidation関連クラス

ここまでで属性チェックによるエラーメッセージを任意の箇所に表示させることができたが、今のままだと以下のようにPOST前の状態でもエラーメッセージが表示されっぱなしになる。
キャプチャ5.PNG

GET時やエラーがない場合は非表示にしたい。
 ⇒@Html.ValidationSummary()Html.ValidationMessageFor()
  自動で付与してくれるクラスを利用する。

@Html.ValidationSummary()およびHtml.ValidationMessageFor()は、プロパティのエラーの有無によって自動的にHtmlタグへクラスを付与してくれる。
付与されるクラスの一覧は以下。

  • validation-summary-valid:エラーがない場合、@Html.ValidationSummary()の生成したdiv要素に付与。
  • validation-summary-errors:エラーがある場合、@Html.ValidationSummary()の生成したdiv要素に付与。
  • input-validation-error:エラーがある場合、エラーとなったプロパティを使用している要素に付与。
  • field-validation-valid:エラーがない場合、Html.ValidationMessageFor()の生成したspan要素に付与。
  • field-validation-error:エラーがある場合、Html.ValidationMessageFor()の生成したspsn要素に付与

つまり、class="validation-summary-valid"の要素は非表示とし、class="validation-summary-errors"の要素は表示させればよいことになる。
これらは基本的にSite.css内に記載する。

参考サイトによると以前はSite.cssに以下のような記述がデフォルトで用意されていたようだが、現在は自分で記載する必用があるようだ。
(もしかして今は別の方法がスタンダードになっている?)

Site.css
.field-validation-error
{
  color: #ff0000;
}

.field-validation-valid
{
  display: none;
}

.input-validation-error
{
  border: 1px solid #ff0000;
  background-color: #ffeeee;
}

.validation-summary-errors
{
  font-weight: bold;
  color: #ff0000;
}

.validation-summary-valid
{
  display: none;
}

この状態でビルドすると、当然以下のように必用な時のみ表示させることができる。
(ついでにエラーメッセージが赤字になっている)

GET時
キャプチャ6.PNG

POST時 桁数チェックエラー
キャプチャ7.PNG


参考サイト:@IT 連載:ASP.NET MVC入門

  • このエントリーをはてなブックマークに追加
  • Qiitaで続きを読む

【Xamarin】C#でAndroidアプリを作ってみる 2 (デバッグ実行)

概要

前回:C#でAndroidアプリを作ってみる 1 (Xamarinインストール)
今回は、ZodiacSignAppを実機でデバッグ実行してみます。

プロジェクトのプロパティを設定する

  1. ZodiacSignAppプロジェクトのプロパティを開きます。
  2. 「アプリケーション」の「Androidバージョンを使用したコンパイル:(ターゲットフレームワーク)」を選択します。
    プロパティ1.PNG
    ※選択したAndroid SDKのバージョンがインストールされていない場合、エラーメッセージに従ってインストールします。

  3. 「Androidマニフェスト」の「ターゲットAndroidバージョン」を選択します。
    プロパティ2.PNG

実機を設定する

  1. 設定メニューの開発者向けオプションをON, USBデバッグをONにします。
    USBデバッグ.png
    ※機種によって異なります。

  2. データ転送用のUSBケーブルでPCと接続します。

デバッグ実行する

  1. 実機を選択して実行します。 デバッグ実行開始.PNG
  2. 実機で、下図のように表示されれば成功です。
    デバッグ実行結果.png

まとめ

今回は、ZodiacSignAppを実機で動かしてみるところまでを行いました。
次回は、ZodiacSignApp(干支×星座算出アプリ)の画面を作ります。
次回分はこちら⇒C#でAndroidアプリを作ってみる 3 (画面作成1)

  • このエントリーをはてなブックマークに追加
  • Qiitaで続きを読む