20201121のC#に関する記事は6件です。

Bing Search v7 クライアント ライブラリがないので、実装してみた

はじめに

Azure Cognitive Services のカテゴリの一つに Web 検索があります。Web 検索では、Microsoft 社の Web 検索エンジンである Bing の検索を API 経由で利用できる Bing Search API がありましたが、これが、2020 年 10 月 30 日以降、利用できなくなったようです。
image.png
これ以前に、Azure Cognitive Services 内で Bing Search をインスタンス化している場合は、以降 3 年間は利用できるようですが、Azure Cognitive Services の Bing Search を新規に作成、利用することはできなくなりました。

今後、新規に Bing Search API を利用するには、Azure マーケットプレイスにある "Bing Search v7" を利用します。公式ページでも新規利用時のサービス有効化方法として、以下のページが案内されています。

これまでとの変更点としては、Bing Search API のエンドポイントが、以下のように変更されました。ドメインの cognitive 部分が、bing に変更されています。

.NET から、Bing Search API を呼び出す場合は、SDK(クライアント ライブラリ) が利用できました。WebSearch を行いたい場合は、"Microsoft.Azure.CognitiveServices.Search.WebSearch" というライブラリが NuGet 経由で入手可能で、これを使って非常に簡単に、Bing Search API を利用できたのですが、2020 年 11 月 21 日時点では、新しいエンドポイントに対して、このライブラリが正しく動作しません。
現状では、Bing Search API クライアント ライブラリの最終更新日は、2018/03/22 となっており、今回の移行にあわせたクライアント ライブラリの更新が行われていないようでした。
image.png

よって、今回は、Bing Search API をクライアント ライブラリを使わず、利用する方法を説明したいと思います。HttpClient で、直接 API をコールします。今回は、.NET 5 を使用しましたが、.NET Core 3.1 でも動作すると思います。

Bing Search v7 の有効化

Azure ポータルの [リソースの作成] から、"bing search" を検索し、[Bing Search v7] を選択して、Bing Search のインスタンスを作成します。
image.png
価格レベルについては、以下のサイトに記載があります。無料の Free も選択できて、1,000 トランザクション/月まで利用が可能です。

Bing Search のインスタンスが作成されたら、[キーとエンドポイント] を選択し、[キー 1] または、[キー 2] の文字列をコピーしておきます。このキーは、後ほど、API 呼び出しの際に、HTTP ヘッダーに入れて使用します。
image.png

BingClient クライアント ライブラリの実装

以降で、.NET Core コンソール アプリ(.NET 5)で、実装を行っていきます。
まず、BingClient クラスと、検索結果を格納する各クラスを定義します。

BingClient.cs
public class BingClient
{
    // Web ページ検索結果
    public class WebPage
    {
        public string Name;
        public string Url;
        public string Snippet;
        public DateTimeOffset DateLastCrawled;
    }

    // 画像検索結果
    public class Image
    {
        public string Name;
        public string ThumbnailUrl;
        public string ContentUrl;
        public DateTimeOffset DatePublished;
    }

    // 動画検索結果
    public class Video
    {
        public string Name;
        public string ThumbnailUrl;
        public string ContentUrl;
        public string Description;
        public string Publisher;
        public string Creator;
        public long? ViewCount;
        public DateTimeOffset DatePublished;
    }
}

次に、BingClient クラスのプライベートフィールドとコンストラクタを実装します。コンストラクタでは、Bing Search API のサブスクリプション キーを受け取りフィールドに保存しておきます。
また、HTTP 要求のために、HttpClient のインスタンスもフィールドに保存しておきます。

BingClient.cs
public class BingClient
{
    ....

    private static readonly HttpClient _httpClient = new HttpClient();

    private readonly string _endpoint = "https://api.bing.microsoft.com/v7.0";
    private readonly string _subscriptionKey = null;

    public BingClient(string subscriptionKey)
    {
        this._subscriptionKey = subscriptionKey;
    }
}

次に、BingClient クラスに各検索を実行するメソッドを実装していきます。レスポンスは、JSON で返ってきますので、Json.NET で処理します。コードを実装する前に、Json.NET(Newtonsoft.Json) を NuGet パッケージでインストールしておいてください。
先に、Search メソッドで、各検索要求の共通部分を実装しておきます。

BingClient.cs
public class BingClient
{
    ....

    private async Task<dynamic> Search(string url, string subscriptionKey)
    {
        HttpRequestMessage request = new HttpRequestMessage(HttpMethod.Get, new Uri(url));
        request.Headers.Add("Ocp-Apim-Subscription-Key", subscriptionKey);

        HttpResponseMessage response = await _httpClient.SendAsync(request);
        response.EnsureSuccessStatusCode();

        string responseString = await response.Content.ReadAsStringAsync();
        dynamic json = JsonConvert.DeserializeObject<dynamic>(responseString);

        return json;
    }
}

次に、Web ページ検索を実行する SearchWeb メソッドを実装します。引数に、検索キーワード、マーケットコード、検索結果数をとります。
マーケットコードは、Market and language codes used by Bing Web Search API に定義があります。
また、検索結果の各プロパティは、最小限のものを入れていますので、必要に応じて、Web Search API v7 response objects
の "WebPage" を参照して、クラス、処理を拡張してください。

BingClient.cs
public class BingClient
{
    ....

    public async Task<IEnumerable<WebPage>> SearchWeb(string keyword, string market = "ja-JP", int resultCount = 10)
    {
        string endpoint = $"{_endpoint}/search";
        string url = $"{endpoint}?q={Uri.EscapeDataString(keyword)}&mkt={market}&responseFilter=Webpages&count={resultCount}";

        dynamic json = await this.Search(url, _subscriptionKey);

        var webPages = (json.webPages.value as IEnumerable<dynamic>).Select(page =>
            new WebPage()
            {
                Name = page.name,
                Url = page.url,
                Snippet = page.snippet,
                DateLastCrawled = DateTime.SpecifyKind(page.dateLastCrawled.Value, DateTimeKind.Utc),
            });

        return webPages;
    }
}

同様に、画像検索、動画検索も実装していきます。
画像検索の結果の各プロパティの定義は、Image Search APIs v7 response objects の Image に、動画検索の結果の各プロパティは、Video Search APIs v7 response objects
の Video を参照してください。

BingClient.cs
public class BingClient
{
    ....

    public async Task<IEnumerable<Image>> SearchImage(string keyword, string market = "ja-JP", int resultCount = 10)
    {
        string endpoint = $"{_endpoint}/images/search";
        string url = $"{endpoint}?q={Uri.EscapeDataString(keyword)}&mkt={market}&count={resultCount}";

        dynamic json = await this.Search(url, _subscriptionKey);

        var images = (json.value as IEnumerable<dynamic>).Select(img => 
            new Image()
            {
                Name = img.name,
                ThumbnailUrl = img.thumbnailUrl,
                ContentUrl = img.contentUrl,
                DatePublished = DateTime.SpecifyKind(img.datePublished.Value, DateTimeKind.Utc),
            });

        return images;
    }

    public async Task<IEnumerable<Video>> SearchVideo(string keyword, string market = "ja-JP", int resultCount = 10)
    {
        string endpoint = $"{_endpoint}/videos/search";
        string url = $"{endpoint}?q={Uri.EscapeDataString(keyword)}&mkt={market}&count={resultCount}";

        dynamic json = await this.Search(url, _subscriptionKey);

        var videos = (json.value as IEnumerable<dynamic>).Select(vdo =>
            new Video()
            {
                Name = vdo.name,
                ThumbnailUrl = vdo.thumbnailUrl,
                ContentUrl = vdo.contentUrl,
                Description = vdo.description,
                Publisher = vdo.publisher?[0]?.name,
                Creator = vdo.creator?.name,
                ViewCount = vdo.viewCount,
                DatePublished = DateTime.SpecifyKind(vdo.datePublished.Value, DateTimeKind.Utc),
            });           

        return videos;
    }

BingClient クライアント ライブラリの利用

実装した BingClient クライアント ライブラリを利用します。
"yourKey" には、先に Azure ポータルで有効化した Bing Search v7 のキーを設定してください。

Program.cs
class Program
{
    static async Task Main(string[] args)
    {
        string subscriptionKey = "<yourKey>";            
        string keyword = "猫";

        BingClient bingClient = new BingClient(subscriptionKey);

        var webPages = await bingClient.SearchWeb(keyword);
        webPages.ToList().ForEach(w => 
        {
            Console.WriteLine($"ページ タイトル: {w.Name}");
            Console.WriteLine($"概要: {w.Snippet}");
            Console.WriteLine($"URL: {w.Url}");
            Console.WriteLine();
        });            

        var images = await bingClient.SearchImage(keyword);
        images.ToList().ForEach(img =>
        {
            Console.WriteLine($"画像タイトル: {img.Name}");
            Console.WriteLine($"URL: {img.ContentUrl}");
            Console.WriteLine();
        });

        await Task.Delay(1000);

        var videos = await bingClient.SearchVideo(keyword);
        videos.ToList().ForEach(v =>
        {
            Console.WriteLine($"動画タイトル: {v.Name}");
            Console.WriteLine($"概要: {v.Description}");
            Console.WriteLine($"サムネイル URL: {v.ThumbnailUrl}");
            Console.WriteLine($"URL: {v.ContentUrl}");
            Console.WriteLine($"視聴数: {v.ViewCount}");
            Console.WriteLine();
        });

        var news = await bingClient.SearchNews(keyword);
        news.ToList().ForEach(n =>
        {
            Console.WriteLine($"ニュース タイトル: {n.Name}");
            Console.WriteLine($"概要: {n.Description}");
            Console.WriteLine($"サムネイル URL: {n.ThumbnailUrl}");
            Console.WriteLine($"URL: {n.Url}");                
            Console.WriteLine();
        });

        Console.ReadKey();
    }
}

実行結果は以下のようになります。
image.png

完全なコードは、以下の GitHub リポジトリに登録してあります。

https://github.com/Hiromasa-Masuda/BingSearchClientLibrary

参考サイト

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

【Unity】Getcomponentがめんどくさいのでpublic staticで爆速でコードを書く。

public staticに至った経緯

Unityでゲームを作っているときにほぼ必須のGetcomponent。
自分はスクリプトの変数違うスクリプトから取得したいときによく使うのですが、毎回GetComponent<>()とかを使って、<>←これの中にはクラス名をいれて...などとしていては、とても面倒ですし他にやり方がないものか、と思っていたら、シングルトン1であるならば、public staticで直接他のスクリプトから変数を取得できるやり方があるらしいです。

書いてみた

SingletonScript.C#
public class Player : MonoBehaviour
{
  public static Player player;  //public staticして、クラスの名前を書いて、適当な変数(ここではplayerとする)を宣言
  public int playerHp = 100;
  void Start(){
    player = this;
  }
}

このスクリプトのplayerHpを他のスクリプト(ここでは敵スクリプト)から取得したい場合、

プレイヤーの体力変数が欲しい敵.C#
public class Enemy : MonoBehaviour
{
  void Start(){
    Player.player.playerHp--;//クラス名.変数名.変数で、Playerクラスの変数を取得できる。Playerで宣言したこと(int playerHp等)がplayerに入っているイメージ
  }
}

スクリプトの内容は、Startが呼び出されたときにplayerHpから1引くという処理になります。
この場合、Playerの中身が入っているplayerの中のplayerHpを取得したいので . ←をつけています。
ただ、class Playerのスクリプトにplayer = this;という記述があります。
これは何なのかというと、playerという変数をインスタンス化しているようです。もともとPlayerというクラスで宣言したことは設計図のような状態なので、他のスクリプトがその設計図を取得したい場合はインスタンス化をする必要があり、thisという自分自身のインスタンスを表すものをplayerに代入して、設計図からインスタンス化をさせているわけですね。

注意点

public staticはとても便利ですが、似ているクラス名(enemy1 enemy2など)をpublic staticしてしまうと、いざ使ってみようと思った時に検索候補で訳がわからなくなってしまうので、クラス名は間違えにくい名前を付けましょう。

終わり

拙い記事ですが読んでいただきありがとうございました。


  1. シングルトンとはクラスのインスタンスが一つだけになるように設計すること 

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

Getcomponentがめんどくさいのでpublic staticで爆速でコードを書く。【Unity】

public staticに至った経緯

Unityでゲームを作っているときにほぼ必須のGetcomponent。
自分はスクリプトの変数違うスクリプトから取得したいときによく使うのですが、毎回GetComponent<>()とかを使って、<>←これの中にはクラス名をいれて...などとしていては、とても面倒ですし他にやり方がないものか、と思っていたら、シングルトン1であるならば、public staticで直接他のスクリプトから変数を取得できるやり方があるらしいです。

書いてみた

SingletonScript.C#
public class Player : MonoBehaviour
{
  public static Player player;  //public staticして、クラスの名前を書いて、適当な変数(ここではplayerとする)を宣言
  public int playerHp = 100;
  void Start(){
    player = this;
  }
}

このスクリプトのplayerHpを他のスクリプト(ここでは敵スクリプト)から取得したい場合、

プレイヤーの体力変数が欲しい敵.C#
public class Enemy : MonoBehaviour
{
  void Start(){
    Player.player.playerHp--;//クラス名.変数名.変数で、Playerクラスの変数を取得できる。Playerで宣言したこと(int playerHp等)がplayerに入っているイメージ
  }
}

スクリプトの内容は、Startが呼び出されたときにplayerHpから1引くという処理になります。
この場合、Playerの中身が入っているplayerの中のplayerHpを取得したいので . ←をつけています。
ただ、class Playerのスクリプトにplayer = this;という記述があります。
これは何なのかというと、playerという変数をインスタンス化しているようです。もともとPlayerというクラスで宣言したことは設計図のような状態なので、他のスクリプトがその設計図を取得したい場合はインスタンス化をする必要があり、thisという自分自身のインスタンスを表すものをplayerに代入して、設計図からインスタンス化をさせているわけですね。

注意点

public staticはとても便利ですが、似ているクラス名(enemy1 enemy2など)をpublic staticしてしまうと、いざ使ってみようと思った時に検索候補で訳がわからなくなってしまうので、クラス名は間違えにくい名前を付けましょう。

終わり

拙い記事ですが読んでいただきありがとうございました。


  1. シングルトンとはクラスのインスタンスが一つだけになるように設計すること 

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

C# - xmlファイルへ設定情報(自作クラスのフィールド・プロパティ)を読み書きする/色々な値を入れて実験してみた

忙しい人向け ⇒ 3章 まとめ

1. 基本

1.1. 超概要

XmlSerializerクラスを使うと、

C#
[Serializable]
public class なんちゃら
{
    public 型名 かんちゃら;
}

みたいな自作クラスのインスタンスの値を下記のようにXMLに保存したり、復元(XMLファイルの値に基づいて自作クラスのメンバに値を代入済みのインスタンスを生成)したりできます。

XMLファイルの中身
<?xml version="1.0" encoding="utf-8"?>
<なんちゃら xmlns:xsd="http://www.w3.org/2001/XMLSchema" xmlns:xsi="http://www.w3.org/2001/XMLSchema-instance">
  <かんちゃら>ほげ</かんちゃら>
</なんちゃら>

1.2. やりかた(参考サイト)

丸投げ・・・

1.3. XMLに保存される対象

publicなフィールドと、get/setともpublicなプロパティが保存対象のようです。

XmlSerializer.Serialize Method (System.Xml.Serialization) | Microsoft Docs より

The Serialize method converts the public fields and read/write properties of an object into XML. It does not convert methods, indexers, private fields, or read-only properties. To serialize all an object's fields and properties, both public and private, use the BinaryFormatter.

In the xmlWriter parameter, specify an object that derives from the abstract XmlWriter class. The XmlTextWriter derives from the XmlWriter.

Note
The XmlSerializer cannot serialize the following: arrays of ArrayList and arrays of List.

Google翻訳結果:

Serializeメソッドは、オブジェクトのパブリックフィールドと読み取り/書き込みプロパティをXMLに変換します。メソッド、インデクサー、プライベートフィールド、または読み取り専用プロパティは変換されません。パブリックとプライベートの両方のすべてのオブジェクトのフィールドとプロパティをシリアル化するには、BinaryFormatterを使用します。

xmlWriterパラメーターで、抽象XmlWriterクラスから派生するオブジェクトを指定します。 XmlTextWriterはXmlWriterから派生しています。

注意
XmlSerializerは、ArrayListの配列とList の配列をシリアル化できません。

1.4. 注意すべきこと

1.4.1. 使用上(システム構築/運用上)の注意

XMLファイルはただのテキストファイルであり、直接ユーザーが編集できてしまうので、設定ファイルを切り出すことで、セキュリティ上のリスクや、実行時エラーなどのリスクを生むかもしれません。1
また、C#など.Net系の言語をビルドしたexeファイルは逆アセンブルが容易に可能なため、ハッシュ値などのチェック値を入れるなどの対策だけだと、完全にはガードできないものと思います。

1.4.2. 実行ファイルとの相対パスに置きたい場合の注意

1.4.3. その他

  • 文字コード(UTF-8のBOMの有無にも念のため注意)
  • 例外処理(例:ファイルを開いていて書き込めないとき・・・System.IO.IOException)のケア
  • nullの処置
  • 浮動小数点数を10進数表示することによる誤差
  • エスケープが必要な文字の扱い(XMLを直接編集する場合)

番外:

  • コマンドプロンプト上で日本語等のマルチバイト文字を出力させて試す場合は、コマンドプロンプトのコードページの変更が必要な場合があります。

2. 本記事の本題

自作ツールを作っている最中に、設定ファイルを切り出したくなることがよくあります。
デフォルトの設定値を埋め込んだXMLファイルを作りたいけど、それ用のソースコードを用意するのが面倒。
なので、どういう自作クラスを書くとどういうXMLが生成されるのかをテンプレとして整理してみます。
色々試した結果をおいておきます。

2.1. メンバが数値や文字列などの基本的な型の場合

2.1.1. XMLへの書き込み

C#ソースコード
using System;
using System.IO;

[Serializable]
public class SampleClass
{
    public string   SampleFieldString;
    public int      SampleFieldInt;
    public uint     SampleFieldUInt;
    public long     SampleFieldLong;
    public bool     SampleFieldBool;
    public double   SampleFieldDouble;
    public float    SampleFieldFloat;
    public char     SampleFieldChar;
    public Decimal  SampleFieldDecimal;
    public DateTime SampleFieldDateTime;
}

class SerializerSample
{
    [STAThread]
    static void Main()
    {
        var sampleValues = new SampleClass(){
            SampleFieldString   = "hoge",
            SampleFieldInt      = -1,
            SampleFieldUInt     =  1,
            SampleFieldLong     =  2,
            SampleFieldBool     =  false,
            SampleFieldDouble   =  1.2d,
            SampleFieldFloat    =  1.3f,
            SampleFieldChar     =  'c',
            SampleFieldDecimal  =  1.4m,
            SampleFieldDateTime =  DateTime.Now,
        };

        var serializer = new System.Xml.Serialization.XmlSerializer(typeof(SampleClass));
        using ( var sw = new System.IO.StreamWriter("SampleOutput.xml", false, new System.Text.UTF8Encoding(false)) ) // BOMなしUTF-8
        {
            serializer.Serialize(sw, sampleValues);
        }
    }
}
生成されたXMLファイルSampleOutput.xml
<?xml version="1.0" encoding="utf-8"?>
<SampleClass xmlns:xsd="http://www.w3.org/2001/XMLSchema" xmlns:xsi="http://www.w3.org/2001/XMLSchema-instance">
  <SampleFieldString>hoge</SampleFieldString>
  <SampleFieldInt>-1</SampleFieldInt>
  <SampleFieldUInt>1</SampleFieldUInt>
  <SampleFieldLong>2</SampleFieldLong>
  <SampleFieldBool>false</SampleFieldBool>
  <SampleFieldDouble>1.2</SampleFieldDouble>
  <SampleFieldFloat>1.3</SampleFieldFloat>
  <SampleFieldChar>99</SampleFieldChar>
  <SampleFieldDecimal>1.4</SampleFieldDecimal>
  <SampleFieldDateTime>2020-11-21T14:52:46.6364558+09:00</SampleFieldDateTime>
</SampleClass>

生成されたXMLファイルには型情報は含まれないようです。
(復元するときにチェックされるものと思われますが、調べてないので不明です。)

2.2.1.1. nullの扱い

string型のフィールドであるSampleFieldStringnullを代入しておいてXMLを生成したところ、
生成されたXMLからSampleFieldStringがいなくなりました。

生成されたXMLファイル
<?xml version="1.0" encoding="utf-8"?>
<SampleClass xmlns:xsd="http://www.w3.org/2001/XMLSchema" xmlns:xsi="http://www.w3.org/2001/XMLSchema-instance">
  <SampleFieldInt>-1</SampleFieldInt>
  <SampleFieldUInt>1</SampleFieldUInt>
  <SampleFieldLong>2</SampleFieldLong>
  <SampleFieldBool>false</SampleFieldBool>
  <SampleFieldDouble>1.2</SampleFieldDouble>
  <SampleFieldFloat>1.3</SampleFieldFloat>
  <SampleFieldChar>99</SampleFieldChar>
  <SampleFieldDecimal>1.4</SampleFieldDecimal>
  <SampleFieldDateTime>2020-11-21T15:15:07.2962926+09:00</SampleFieldDateTime>
</SampleClass>

2.2.1.2. エスケープされそうな文字を入れてみる

C#抜粋
SampleFieldString   = "<hoge=foo\"bar\"piyo?piyo!hoge2 hoge3/hoge4\nhoge5\r\nhoge6\thoge7&hoge8;hoge9>",
生成されたXMLファイル抜粋
  <SampleFieldString>&lt;hoge=foo"bar"piyo?piyo!hoge2 hoge3/hoge4
hoge5
hoge6   hoge7&amp;hoge8;hoge9&gt;</SampleFieldString>

<>&は、それぞれ&lt;, &gt;, &amp;というHTMLっぽい感じにエスケープされています。
改行コード\r,\nとタブ\tはそのまま出力されるようです。

2.2.2. XMLからの復元

C#
using System;
using System.IO;

[Serializable]
public class SampleClass
{
    同上のため省略
}

class SerializerSample
{
    [STAThread]
    static void Main()
    {
        SampleClass sampleValues;

        var serializer = new System.Xml.Serialization.XmlSerializer(typeof(SampleClass));
        using (var streamReader = new System.IO.StreamReader("SampleOutput.xml", System.Text.Encoding.UTF8))
        using (var xmlReader = System.Xml.XmlReader.Create(streamReader))
        {
            sampleValues = (SampleClass)serializer.Deserialize(xmlReader);
        }

        Console.Write("SampleFieldString  :");   Console.WriteLine(sampleValues.SampleFieldString  );
        Console.Write("SampleFieldInt     :");   Console.WriteLine(sampleValues.SampleFieldInt     );
        Console.Write("SampleFieldUInt    :");   Console.WriteLine(sampleValues.SampleFieldUInt    );
        Console.Write("SampleFieldLong    :");   Console.WriteLine(sampleValues.SampleFieldLong    );
        Console.Write("SampleFieldBool    :");   Console.WriteLine(sampleValues.SampleFieldBool    );
        Console.Write("SampleFieldDouble  :");   Console.WriteLine(sampleValues.SampleFieldDouble  );
        Console.Write("SampleFieldFloat   :");   Console.WriteLine(sampleValues.SampleFieldFloat   );
        Console.Write("SampleFieldChar    :");   Console.WriteLine(sampleValues.SampleFieldChar    );
        Console.Write("SampleFieldDecimal :");   Console.WriteLine(sampleValues.SampleFieldDecimal );
        Console.Write("SampleFieldDateTime:");   Console.WriteLine(sampleValues.SampleFieldDateTime);
    }
}
実行結果
SampleFieldString  :hoge
SampleFieldInt     :-1
SampleFieldUInt    :1
SampleFieldLong    :2
SampleFieldBool    :False
SampleFieldDouble  :1.2
SampleFieldFloat   :1.3
SampleFieldChar    :c
SampleFieldDecimal :1.4
SampleFieldDateTime:2020/11/21 14:52:46

2.2. メンバが配列やリストの場合

C#抜粋
using System;
using System.IO;
using System.Collections.Generic;

[Serializable]
public class SampleClass
{
    public List<string>  SampleFieldStringList;
    public string[]      SampleFieldStringArray;
    public List<int>     SampleFieldIntList;
    public int[]         SampleFieldIntArray;
    public List<long>    SampleFieldLongList;
    public long[]        SampleFieldLongArray;
    public List<decimal> SampleFieldDecimalList;
    public decimal[]     SampleFieldDecimalArray;
}

class SerializerSample
{
    [STAThread]
    static void Main()
    {
        var sampleValues = new SampleClass(){
            SampleFieldStringList  = new List<string>(){"hoge1","foo","bar"},
            SampleFieldStringArray =       new string[]{"hoge2","foo","bar"},
            SampleFieldIntList     = new List<int>(){1,2},
            SampleFieldIntArray    =       new int[]{3,4},
            SampleFieldLongList    = new List<long>(){5,6},
            SampleFieldLongArray   =       new long[]{7,8},
            SampleFieldDecimalList = new List<decimal>(){0.1m,0.2m},
            SampleFieldDecimalArray=       new decimal[]{0.3m,0.4m},
        };
        以下略(本記事の2.1.1.章参照)
    }
}
生成されたXMLファイル
<?xml version="1.0" encoding="utf-8"?>
<SampleClass xmlns:xsd="http://www.w3.org/2001/XMLSchema" xmlns:xsi="http://www.w3.org/2001/XMLSchema-instance">
  <SampleFieldStringList>
    <string>hoge1</string>
    <string>foo</string>
    <string>bar</string>
  </SampleFieldStringList>
  <SampleFieldStringArray>
    <string>hoge2</string>
    <string>foo</string>
    <string>bar</string>
  </SampleFieldStringArray>
  <SampleFieldIntList>
    <int>1</int>
    <int>2</int>
  </SampleFieldIntList>
  <SampleFieldIntArray>
    <int>3</int>
    <int>4</int>
  </SampleFieldIntArray>
  <SampleFieldLongList>
    <long>5</long>
    <long>6</long>
  </SampleFieldLongList>
  <SampleFieldLongArray>
    <long>7</long>
    <long>8</long>
  </SampleFieldLongArray>
  <SampleFieldDecimalList>
    <decimal>0.1</decimal>
    <decimal>0.2</decimal>
  </SampleFieldDecimalList>
  <SampleFieldDecimalArray>
    <decimal>0.3</decimal>
    <decimal>0.4</decimal>
  </SampleFieldDecimalArray>
</SampleClass>
  • 配列とList<>は同じ出力になるようです。
  • <メンバ名><要素の型名>値</要素の型名>・・・</メンバ名> となるようです。
    (C#以外(VB.Netとか)だとどうなるのだろうか?)

2.3. メンバの型が自作クラスの場合

C#
using System;
using System.IO;

[Serializable]
public class SampleClass
{
    public SampleChildClass SampleFieldChildMember;
}

[Serializable]
public class SampleChildClass
{
    public string SampleFieldString;
}

class SerializerSample
{
    [STAThread]
    static void Main()
    {
        var sampleValues = new SampleClass(){
            SampleFieldChildMember  = new SampleChildClass(){SampleFieldString="hoge"},
        };
        以下略(本記事の2.1.1.章参照)
    }
}
生成されたXMLファイル
<?xml version="1.0" encoding="utf-8"?>
<SampleClass xmlns:xsd="http://www.w3.org/2001/XMLSchema" xmlns:xsi="http://www.w3.org/2001/XMLSchema-instance">
  <SampleFieldChildMember>
    <SampleFieldString>hoge</SampleFieldString>
  </SampleFieldChildMember>
</SampleClass>

<メンバ名><子クラスのメンバ名>値</子クラスのメンバ名>・・・</メンバ名> となるようです。
(子クラスのクラス名SampleChildClassは出力されませんでした。)

2.4. メンバが構造体(System.Drawing.PointとかSystem.Drawing.Size)の場合

C#抜粋
using System;
using System.IO;
using System.Drawing;

[Serializable]
public class SampleClass
{
    public Point SampleFieldPoint;
    public Size  SampleFieldSize;
}

class SerializerSample
{
    [STAThread]
    static void Main()
    {
        var sampleValues = new SampleClass(){
            SampleFieldPoint = new Point(10,20),
            SampleFieldSize  = new Size(30,40),
        };
        以下略(本記事の2.1.1.章参照)
    }
}
生成されたXMLファイル
<?xml version="1.0" encoding="utf-8"?>
<SampleClass xmlns:xsd="http://www.w3.org/2001/XMLSchema" xmlns:xsi="http://www.w3.org/2001/XMLSchema-instance">
  <SampleFieldPoint>
    <X>10</X>
    <Y>20</Y>
  </SampleFieldPoint>
  <SampleFieldSize>
    <Width>30</Width>
    <Height>40</Height>
  </SampleFieldSize>
</SampleClass>

2.3章と同様ですね。

2.5. Dictionary<,>・・・そのままだとシリアライズできない

下記エラーがでました。(※見やすさのためエラーメッセージに適宜改行を加えています。)

ハンドルされていない例外: System.InvalidOperationException: 型 'SampleClass' を反映中にエラーが発生しました。
--->
System.NotSupportedException: IDictionary が実装されているため、
型 System.Collections.Generic.Dictionary`2
[[System.String, mscorlib, Version=4.0.0.0, Culture=neutral, PublicKeyToken=b77a5c561934e089],
 [System.String, mscorlib, Version=4.0.0.0, Culture=neutral, PublicKeyToken=b77a5c561934e089]]
のメンバー SampleClass.SampleFieldDict1 はシリアル化できません。

対応されている方がいらっしゃるので参考のためリンクを貼っておきます。

3. 実験結果のまとめ

2章の通り、実験した限りでは下記のようになりました。

  • 値がnullのメンバは出力されない。
  • 文字列の場合、文字列中の<,>,&はそれぞれ&lt;,&gt;,&amp;にエスケープされる。
  • 型情報は(配列やList<>の要素の場合を除き、)基本的に出力されない。
  • メンバの型が配列やList<>の場合
    <メンバ名><要素の型名>値</要素の型名>・・・</メンバ名>
  • メンバの型が構造体やクラスの場合
    <メンバ名><子クラスのメンバ名>値</子クラスのメンバ名>・・・</メンバ名>
  • Dictionary<,>はそのままだとシリアライズできない。(2.5章参照)

  1. コードに直接埋め込んでいても似たようなものかもしれませんが。。 

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

<自作>勉強でStateMachineを制作してみた

初めての投稿です!様々な意見を下さるととてもうれしいです!
今回は現在Unityを使用してゲーム制作をしているのですが、それで自分で制作したStateMachineを公開したいと思います!
プログラム歴約2年ほどですが、いろんな人に参考にしていただけると嬉しいです!

コードはGitHubにも公開しているので良ければ見てみてください!
StateMachine

IState

public interface IState
{
    //始めに呼ばれる関数
    StateTagList Enter(MonoBehaviour mono);
    //毎フレーム呼ばれる関数
    StateTagList Execute(MonoBehaviour mono);
    //消去時に呼ばれる関数
    StateTagList Exit(MonoBehaviour mono);
}

interfaceでStateの抽象クラスを制作しました。
これを継承していろいろなStateを制作していきます。
戻り値をenumのtagにしており戻り値によってStateを変更するようにしています。

StateTagList

//タグと一緒にStateを追加する
public enum StateTagList
{
    none,
    move,
    move1,
}

enumでtagを制作しました。
Stateをリスト化してゲーム初めにすべてのStateをインスタンスしてtagと一緒に管理してやるために制作しました。
Stateの変更がないときはnoneを返すようにします。

IStateを継承して作ったクラス

public class move : IState
{
    GameObject gameObject = null;
    public StateTagList Enter(MonoBehaviour mono)
    {
        gameObject = mono.gameObject;
        return StateTagList.none;
    }
    Vector3 v3 = new Vector3(0.05f, 0, 0);
    public StateTagList Execute(MonoBehaviour mono)
    {
        gameObject.transform.position += v3;
        if (Input.GetKeyDown(KeyCode.C))
        {
            return StateTagList.move1;
        }
        return StateTagList.none;
    }

    public StateTagList Exit(MonoBehaviour mono)
    {
        return StateTagList.none;
    }
}

public class move1 : IState
{
    GameObject gameObject = null;
    public StateTagList Enter(MonoBehaviour mono)
    {
        gameObject = mono.gameObject;
        return StateTagList.none;
    }
    Vector3 v3 = new Vector3(-0.05f, 0, 0);
    public StateTagList Execute(MonoBehaviour mono)
    {
        gameObject.transform.position += v3;
        if (Input.GetKeyDown(KeyCode.C))
        {
            return StateTagList.move;
        }
        return StateTagList.none;
    }

    public StateTagList Exit(MonoBehaviour mono)
    {
        return StateTagList.none;
    }
}


次はIStateを継承して作った子クラスです。
中身は今回はテスト用ということで”C”を押すと移動が左右切り替わるだけのシンプルなものにしています。

StateList

public class StateList
{
    //Dictionaryクラスのオブジェクト生成
    private Dictionary<StateTagList, IState> statelist = new Dictionary<StateTagList, IState>();

    //この関数でStateリストの追加
    public void AddState(StateTagList _tag,IState _state)
    {
        statelist.Add(_tag, _state);
    }

    //ここにタグを引数として入れればそのStateがかえってくる
    public IState GetState(StateTagList _tag)
    {
        return statelist[_tag];
    }

}

次に早速enumで作ったtagを使ってリスト化していきます。
管理方法はDictionaryクラスを使用していきます。
私は今回初めてDictionaryクラスを使ったのですが、とても便利でした!!
今までC++でネイティブ開発をしていたのですが、正直std::mapより使いやすくて好きです!!

StateMachine

public class StateMachine
{
    private IState state = null;

    private StateList stateList = new StateList();

    public StateList GetStateList() { return stateList; }

    public IState GetState() { return state; }

    //Stateを変更する際に通す関数
    //自動でEnterとExitに通るようになっている
    public void ChangeState(StateTagList _tag,MonoBehaviour _mono)
    {

        IState _state = stateList.Getstate(_tag);

        state?.Exit(_mono);
        state = _state;
        state?.Enter(_mono);
    }

    //StateMachineを持たせているscriptのUpdateでこの関数を回してやる
    //引数に自分のGameObjectを持たせてやる
    public void Update(MonoBehaviour _mono)
    {
        if(state != null)
        {
            StateTagList _tag = state.Execute(_mono);   
            if (_tag != StateTagList.none) 
            {
                ChangeState(_tag, _mono);
            }
        }
    }

}

最後に本題のStateMachineです。中身としてはStateの切り替えやStateの管理などをしています。
またStateを切り替えるとEnterとExitをに入るようにしています。
最近知ったのですが、State?.~~~~とすることでnull回避ができると聞いてとても便利だと感じました!!

使用方法

public class EnemyController : MonoBehaviour 
{
    StateMachine state = new StateMachine();
    void Start()
    {
        state.GetStateList().AddState(StateTagList.move, new move());
        state.GetStateList().AddState(StateTagList.move1, new move1());
        state.ChangeState(StateTagList.move1, this);
    }

    void Update()
    {
        state.Update(this);

    }
}

使用方法はシンプルです。StateMachineを変数宣言して初めに使うStateをすべてインスタンスしてListに入れてやります。そして初めに使うStateをChangeStateでセットしてやります。
最後にUpdateで自分自身を引数に入れてUpdateを回してやるだけです!

最後に

今回初めて記事を書いたのですがどうでしょうか?
今回制作したStateMachineはもっと改良すればもっといいものになると思うので今後も改良してみたいと思います。またいろいろな意見を下さるととてもうれしいです!!
最後まで見て下さりありがとうございます!!

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

UWP 新しいウィンドウを表示する。

新規Windowを作成し、表示する方法

2通りの方法がある。

方法1

新規作成されるウィンドウは、もとのウィンドウと同じスレッドで作成される。

window1.cs
var frame = new Frame();
//frame.Navigate(typeof(AnotherPage));

var appWindow = await AppWindow.TryCreateAsync();
ElementCompositionPreview.SetAppWindowContent(appWindow, frame);

await appWindow.TryShowAsync();

方法2

新規作成されるウィンドウは、もとのウィンドウと別のスレッドで作成される。

window2.cs
CoreApplicationView newView = CoreApplication.CreateNewView();

int id = 0;
await newView.Dispatcher.RunAsync(CoreDispatcherPriority.Normal, () =>
{
    var frame = new Frame();
    //frame.Navigate(typeof(AnotherPage));

    Window.Current.Content = frame;
    Window.Current.Activate();
    id = ApplicationView.GetForCurrentView().Id;
});

await ApplicationViewSwitcher.TryShowAsStandaloneAsync(id);

まとめ

方法1
https://docs.microsoft.com/ja-jp/windows/uwp/design/layout/app-window

方法2
https://docs.microsoft.com/ja-jp/windows/uwp/design/layout/application-view

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