20200714のC#に関する記事は10件です。

[C#] 静的メンバと静的クラス

静的メンバ

  • 特定のインスタンスにではなく,クラスに属するフィールドやメソッド,プロパティのこと.
  • static修飾子をつけると静的メンバを宣言できる.
  • 静的メンバはアプリケーションにただ1つだけ生成され,すべてのインスタンスの間で共有される.
  • 静的メンバへのアクセスには,クラスインスタンスを生成するのではなく,クラス名を直接指定する.
  • 静的フィールドの初期化には,静的コンストラクターを利用する.
  • 通常のコンストラクターがインスタンス生成時に毎回呼び出されるのに対して, 静的コンストラクターはそのクラスの何れかのメンバに初めてアクセスしたときに1度だけ呼び出される.
  • 静的メンバから,通常のインスタンス変数やインスタンスメソッドへはアクセスできない.

実装例:静的メンバの作成

  • 「果物」を表すFruitクラスに対し,番号プロパティint Id, 名前プロパティstring Name,コンストラクタ,文字列変換メソッドstring ToString()を与える.
    • これらは,インスタンスに依存する通常のローカルメンバであり,インスタンス毎に異なる値を持つ.
  • 番号から果物への単射影を管理するためのprivate静的フィールドDictionary<int, Fruit> _fruitsを与える.
  • 静的コンストラクタを用意し,_fruitsへ初期値を与える.
  • 番号指定でFruitクラスインスタンスを作成するためのFromId(int id)静的メソッドを与える.
    • これらの静的メンバは,インスタンスに依存せずクラス間で共有される.
/// <summary>
/// 果物を表します.
/// </summary>
public class Fruit
{
    /// <summary>
    /// 番号
    /// </summary>
    public int Id { get; set; }

    /// <summary>
    /// 名前
    /// </summary>
    public string Name { get; set; }

    /// <summary>
    /// 果物図鑑 ※静的メンバ(=クラス間で共通)
    /// </summary>
    private static Dictionary<int, Fruit> _fruits { get; set; }

    /// <summary>
    /// コンストラクタ ※静的メンバにはアクセス不可
    /// </summary>
    /// <param name="id">番号</param>
    /// <param name="name">名前</param>
    public Fruit(int id, string name)
    {
        this.Id = id;
        this.Name = name;
    }

    /// <summary>
    /// 静的コンストラクタ ※静的メンバのみにアクセス可能
    /// </summary>
    static Fruit()
    {
        // 果物図鑑を作成
        _fruits = new Fruit[] { new Fruit(1, "りんご"), 
                                new Fruit(2, "いちご"), 
                                new Fruit(3, "メロン") }
                               .ToDictionary(f => f.Id, f => f);
    }

    /// <summary>
    /// 現在のオブジェクトを表す文字列を返します.
    /// </summary>
    public override string ToString()
    {
        return $"{this.Id:00}{this.Name}";
    }

    /// <summary>
    /// 番号を指定して果物図鑑からインスタンスを取得します.
    /// </summary>
    /// <param name="id">番号</param>
    public static Fruit FromId(int id)
    {
        return _fruits[id];
    }
}

実装例:メンバへのアクセス方法

  • 通常のローカルメンバは,クラスインスタンスから呼び出しを行う.
  • 静的メンバは,クラスをインスタンス化せず,[クラス名.メンバ]の記載形式で呼び出しを行う.
// 通常のプロパティやメソッドは,クラスをインスタンス化してからアクセス
var fruit1 = new Fruit(4, "パイナップル");
var id = fruit1.Id;                 // int[4]    
var display = fruit1.ToString();    // string["04:パイナップル"]

// 静的メンバは,クラスをインスタンス化せず,[クラス名.メンバ]の記載形式でアクセス
var fruit2 = Fruit.FromId(2);       // Fruit[Id = 2, Name = "いちご"]
var fruit3 = Fruit.FromId(3);       // Fruit[Id = 3, Name = "メロン"]

静的クラス

  • 静的メンバしか定義できないクラス.
  • クラスの前にstatic修飾子を付けて宣言する.
  • インスタンスの作成が不可能なクラスを作る場合に利用する.
  • 別言語では module と呼ばれ区別されることもある.

staticの活用

  • 例えば,readonlyでないpublicな静的フィールドはどこからでも書き換えられる可能性があり,むやみな使用は控えるべきである.
  • 以下に,static修飾子の活用方法を幾つか示す.

ユーティリティ(ヘルパー)クラスとしての活用

  • 複数の箇所から利用される汎用的な処理等,共通の機能を提供するために静的クラスを利用する.

グローバル定数としての活用

  • 定数値のシンボル名が必要で,その値の型をconst宣言で使用できない場合,またはその値をコンパイル時に計算できない場合はstatic readonlyフィールドを利用する.
  • constは,属性に指定するパラメータや列挙型の定義など,コンパイル時に値が必要な場合にのみ使用する.
フィールド 値の決定 実行速度 switch文 値の割当
const コンパイル時 readonlyより速い 利用可 インスタンスを new した結果は割当不可
static readonly 実行時 constより僅かに遅い 利用不可 インスタンスを new した結果を割当可能/コンストラクタで初期化可能

拡張メソッド

  • 通常の前置き記法である静的メソッドを,インスタンスメソッドと同様に後置き記法で書くことができる.
  • 拡張メソッドの作成には,静的クラスに対して静的メソッドを実装し,先頭の引数に対してthis修飾子を付与する.

実装例

public static class Extensions
{
    /// <summary>
    /// System.Collections.Generic.IEnumerable`1 型の構築された System.String コレクションのメンバーを連結します.各メンバーの間には,指定した区切り記号が挿入されます.
    /// </summary>
    /// <param name="values">連結する文字列を格納しているコレクション.</param>
    /// <param name="separater">区切り文字として使用する文字列.戻される文字列に separator が含まれるのは,values に複数の要素がある場合のみです.</param>
    /// <returns>values のメンバーからなる,separator 文字列で区切られた文字列. values にメンバーがない場合,メソッドは System.String.Emptyを返します.</returns>
    public static string Join(this IEnumerable<string> values, string separater)
    {
        return string.Join(separater, values);
    }
}

使用例

var pieces = new List<string>() { "飛車", "角", "金", "銀", "桂馬", "香車" };

// 通常の静的メソッド呼び出し(前置き記法)
var display1 = string.Join(", ", pieces);   // string["飛車, 角, 金, 銀, 桂馬, 香車"]

// 拡張メソッド呼び出し(後置き記法)
var display2 = pieces.Join(", ");           // string["飛車, 角, 金, 銀, 桂馬, 香車"]

参考

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

ASP.NET Core MVC 3.1 入門 その5 「View」

はじめに

ASP.NET Core MVC 3.1 の View について、自分が学んだことを備忘録として記載します。
このフレームワークに殆ど触れたことが無い方に少しでも参考になれば幸いです。
誤り等あれば、ご指摘頂けますと大変喜びます。

前回の記事

ASP.NET Core MVC 3.1 入門 その4 「Routing」

今回の流れ

  • MVCパターンの説明
  • Viewの説明
  • Viewを作ってみる
  • 動かしてみる
  • 特殊なViewテンプレートの説明

今回のゴール

  • MVCパターンにおけるViewの役割を理解する
  • Viewを追加できる
  • 特殊なViewテンプレートとその役割を知る

本記事に含まれない内容

以下は別の記事で紹介する予定です。

  • @model @using @inject
  • HTMLヘルパー/タグヘルパー
  • 部分ビュー
  • ビューエンジンとは

環境

IDE
Visual Studio 2019
言語
C#

MVCパターン

コントローラーの説明に入る前に、
まずはMVCパターンについて確認しておきましょう。
全体像を把握したうえで、View について具体的にみていきます。

MVCパターンとは

画面表示と、ビジネスロジックの分離を実現するためのアーキテクチャ(設計手法)です。

要はHTMLの生成といった画面表示のための処理と
DBアクセスのようなデータの管理/操作をするための処理
これらを分離させるために先人が考え出したプログラムの作り方の一つです。

MVCというのは構成要素の略称で、以下の単語の先頭文字をそれぞれ取り出したものです。

Model – View – Controller

実現できると、それぞれが分離しているので変更・修正があった場合、お互いに影響を受けづらく、Testability (テスト容易性) が高まるというメリットがあります。

画面に表示する項目の書式変更によってDBアクセス処理に影響が出たり、
DBの軽微な定義変更によって画面表示処理に影響が出るといった事態が発生しづらくなります。

各要素の役割

要素 概要
Model データの管理/操作といったビジネスロジックを担当
View データ入力用のフォーム、処理結果の表示といったアプリケーションのフロントエンドを担当
Controller ModelとViewの橋渡しを担当

View

Viewとは

MVCの全体像を把握したうえで、 View について具体的にみていきます。
MVCにおいて、 データ入力用のフォーム、処理結果の表示といったアプリケーションのフロントエンドを担当する要素です。

画面(HTML)の生成

というのが主なお仕事です。
後述するViewテンプレートとControllerから渡されるデータを組み合わせて出力するHTMLを生成します。

Viewの構成要素

Viewエンジン

Viewメソッドの呼び出しによって起動し、
Viewテンプレートと、Controllerから渡されるデータを組み合わせて出力するHTMLを生成します。
ASP.NET Core MVC では「Razor」が既定のViewエンジンとして採用されています。

Viewテンプレート

拡張子が「.cshtml」のファイルです。

配置場所と命名

/Views/コントローラー名/アクションメソッド名.cshtml

とすることをおすすめします。
アクションメソッドで呼び出されるViewメソッドが
明示的に指定しない限り、/Views/コントローラー名/アクションメソッド名.cshtmlを読み込むためです。

image.png

一部の特殊なテンプレートについては、ViewsまたはViews/Sharedに配置します。特殊なテンプレートについては、後述します。

image.png

Viewスクリプト

Viewテンプレート内に記述するスクリプトです。
Viewエンジン固有の言語(Razor)で記述していきます。

Viewを追加する

では実際にViewを作ってみましょう。

ASP.NET Core MVC 3.1 のプロジェクトを作成する

まずは「Web アプリケーション(モデル ビュー コントローラー)」テンプレートでプロジェクトを作成します。

image.png

ソリューションエクスプローラーを眺める

各フォルダ/ファイルの概要についてはこちらの記事をご参照ください。
前述したとおり、Viewテンプレートは/Views/コントローラー名/アクションメソッド名.cshtmlに配置するようにしましょう。

image.png

Controller の追加

HelloControllerを追加します。
Controllerについてはこちらの記事をご参照ください。

public class HelloController : Controller
{
    public IActionResult Index()
    {
        return View();
    }
}

View の追加

新しい View を追加してみます。

アクションメソッド(Indexメソッド)のメソッド名を右クリック > ビューの追加

image.png

と進んでください。

スキャフォールディング

以下のようなウィンドウが立ち上がると思います。
「スキャフォールディング」というのは、要は雛形を自動生成する機能のことを示します。
今回はViewの雛形を自動生成してもらうために、この機能を使います。

自動で/Views/コントローラー名/に配置してくれたり、指定したモデルクラスの新規作成(Create)や一覧表示(List)のテンプレート(Bootstrap向けのcssセレクタまで付与されたもの)を用意してくれたりと、手間が省けるので、 Viewを追加する際には、この手順でスキャフォールディング機能を利用することをおすすめします。

今回は「Razor ビュー」> 「Empty(モデルなし)」を選択します。
上述した通り、よりリッチなテンプレートもありますが、まずはシンプルなViewを使っていきましょう。

image.png

続いてビュー名ですが、アクションメソッド名とします。
アクションメソッドで呼び出されるViewメソッドが
明示的に指定しない限り、/Views/コントローラー名/アクションメソッド名.cshtmlを読み込むためです。

image.png

無事にHelloコントローラーのIndexアクションメソッドと対応する View(Index.cshtml) を追加することができました。

image.png

Viewのコードを確認する

以上でViewは追加できたと思います。
では、追加されたViewを見ていきましょう。

Index.cshtml

まず注目して頂きたいのは<h1>タグです。
Viewスクリプトには、お馴染みの HTML/CSS/Javascript をゴリゴリ書いていくことができます。
HTML/CSS/Javascript を用いてページの構成を作りつつ、C#のコードを利用して動的な値を埋め込んでいくというのがView開発の流れになります。

@{
    ViewData["Title"] = "Index";
}

<h1>Index</h1>

次に@ViewDataといった見慣れない構文が目に付くかと思います。これらの構文については後述します。

説明の便宜上、HelloControllerIndex.cshmlに少々手を加えます。

まずはHelloControllerIndexメソッドを以下のように修正します。

public IActionResult Index()
{
    ViewBag.Message = "Hello World!"; //追加
    ViewData["Now"] = DateTime.Now; //追加
    return View();
}

続いてIndex.cshtmlも以下のように修正します。

@{
    ViewData["Title"] = "Index";
}

<h1>Index</h1>

<h2>@ViewBag.Message</h2>
<p>@ViewData["Now"]</p>

動作確認

ここまで来たら、実行してみましょう。
URLを書き換えるのが手間なので、Startup.csConfigureメソッドを以下のように修正し、HelloControllerIndexアクションメソッドが呼び出されるように、Routingの設定をしておきます。
Routingについてはこちらの記事をご参照ください。

public void Configure(IApplicationBuilder app, IWebHostEnvironment env)
{
    //省略

    app.UseEndpoints(endpoints =>
    {
        endpoints.MapControllerRoute(
            name: "default",
            pattern: "{controller=Hello}/{action=Index}/{id?}"); //Home => Hello
    });
}

Indexアクションメソッドで設定した値が画面に表示されれば成功です。

image.png

View変数(ViewBag/ViewData)

<h2>@ViewBag.Message</h2>
<p>@ViewData["Now"]</p>

ViewBagViewDataをView変数と呼びます。
要はViewスクリプトに埋め込む値のことです。
Controller のアクションメソッドから View にデータを渡すことができます。

Controller側で表示に必要なデータを用意しておき、Viewではデータを埋め込む場所や表示方法を定義する

というのが ControllerView の基本的な関係となります。

ViewBag

Controllerでプロパティとして値を設定しておくと、 Viewで参照することができます。
View内で値を設定、参照することも可能です。

型を指定することはできず、Intelisenceも効きません。

public IActionResult Index()
{
    ViewBag.Message = "Hello World!";
    //省略
}
<h2>@ViewBag.Message</h2>

ViewData

Dictionaryとしてキーと値を設定することができます。
用途はViewBagと同様です。やはり型指定もできません。

public IActionResult Index()
{
    ViewData["Now"] = DateTime.Now; //追加
    //省略
}
<p>@ViewData["Now"]</p>

ViewBag vs ViewData

お好みで、どちらを採用しても良いと思います。
ViewBagを使うと、ViewDataDictionaryベースの読みづらいコードを記述する必要はなくなりますが、DLRによって解釈されるコードを使いたくないのであればViewDataをおすすめします。

多用は禁物

View変数の利用は、 Controller から View にデータを渡す最も単純な方法です。

ただし、多用は禁物です。
型の指定ができないので、例えば ControllerView を別の開発者が担当していた場合、 View 側の開発者からすれば、一体どんなデータが設定されてくるのか、見当がつかないかもしれません。

そんな時は、後々記事としてあげますが、強く型指定されたViewModelを利用すべきと筆者は考えます。

@(コードナゲット)

続いて、@について説明していきます。
Viewスクリプトにおいて、C#のコードであることを示すキーワードです。

一行の場合、明示的に閉じる必要はありません。(セミコロンは不要です)コード行がどこで閉じるか解析してくれます。

<h2>@ViewBag.Message</h2>
<p>@ViewData["Now"]</p>

コードブロックを記述したいときは@{...}とします。

@{
    ViewData["Title"] = "Index";
}

特殊なViewテンプレート

特定のアクションメソッドと対応しない特殊なViewが存在します。
ここでは最も基本的な2つを紹介します。

レイアウトページ(_Layout.cshtml)

まずは動作確認

再度実行してみましょう。
以下のような画面が表示されると思います。

image.png

少々違和感があります。
Index.cshtmlに記述したコードはこれだけです。
にも関わらず、それっぽいヘッダーやフッターが付いていますね。

@{
    ViewData["Title"] = "Index";
}

<h1>Index</h1>

<h2>@ViewBag.Message</h2>
<p>@ViewData["Now"]</p>

これはレイアウトページ(_Layout.cshtmlという仕組みによるものです。
複数のページで共通するデザインの外枠を定義するための仕組みです。
レイアウトページを指定しておくと、ヘッダー、フッター、サイドメニューといった共通のデザインを一か所にまとめることができます。

変更する際も、レイアウトページだけを変更すれば一気に適用されるので、保守性も高まります。

レイアウトページはViews/Shared/に格納されています。
レイアウトページだけではなく、複数のViewテンプレートから利用し得るものは基本ここに配置します。

image.png

_Layout.cshtmlのコードを確認しましょう。
headerfooterの間に以下のコードが見つかると思います。

@*省略*@
    <div class="container">
        <main role="main" class="pb-3">
            @RenderBody()
        </main>
    </div>
@*省略*@

重要なのは@RenderBodyという部分です。
ここに個別のViewスクリプト(例えばさきほど作成したIndex.cshtml)が埋め込まれます。

これが、特に記述した記憶のないヘッダーやフッターが付いていた理由です。_Layout.cshtmlに記述されていたというわけです。

_Layout.cshtml

レイアウトページの名称は必ずしも_Layout.cshtmlである必要はありません。また、Views/Shared/に格納しなければならないという規約もありません。
_Layout.cshtmlとはただのViewテンプレートの1つに過ぎないのです。

では、なぜ_Layout.cshtmlがレイアウトページとして検出されているのでしょうか?理由は後述します。

複数のレイアウトページ

これは雑記等、別の記事で紹介しますが、レイアウトページは複数用意することもできます。
例えば、メインのレイアウトページ、管理者用のレイアウトページ、未ログインユーザー向けのレイアウトページなどです。

_ViewStart.cshtml

ViewStart.cshtmlとは、Viewの処理に当たって、最初に処理される特別なファイルです。

重要なのはレイアウトページと異なり、名前空間もファイル名も固定です。

image.png

役割

一般的にはViewで使用するレイアウトページを指定するコードを記述します。実際にテンプレートでも_Layoutをレイアウトページとして扱う旨のコードが記述されていることが確認できます。

ただし、個別のViewでレイアウトページを改めて指定した場合はそちらが優先されます。

@{
    Layout = "_Layout";
}

これが、ただのViewテンプレートの1つに過ぎない_Layout.cshtmlがレイアウトページとして検出されている理由です。_ViewStart.cshtmlで指定されていたというわけです。

他にも、View関連のスタートアップコードが必要な場合はここに記述します。

規約

ファイル名

_ViewStart.cshtml

配置場所(名前空間)

Views/ 直下

以上です。
ありがとうございました。

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

NugetのCoreTweetパッケージを導入する

はじめに

C#アプリでTweet連携をするためのNuGetパッケージを導入してみることにした。

CoreTweetとは

OSSのTwitterライブラリ。2020年7月現在にて、使えるライブラリとされる。

環境

項目 設定値
OS Windows 10
開発環境 Visual Studio Community 2019 version: 16.6.3

1. coreTweetパッケージのインストール

  1. [参照]を選択し、[coreTweet]を検索キーワードとして入力。
    image.png

  2. 左画面にて[CoreTweet]を選択し、インストールするソリューションを選択。[インストール]をクリック。
    image.png

  3. ソリューションに加えられる変更を確認し、[OK]をクリック。
    image.png

  4. インストール済みにバージョン情報が表示されることを確認。
    image.png

参考

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

C#からSlackAPIを利用して、指定期間内に投稿されたメッセージを取得する

はじめに

本記事はC#からSlackAPIを利用して、指定期間内に投稿されたメッセージを取得する方法をまとめることを目的としています。
なお、以前に以下のようなSlackAPIに関する記事を投稿していますので、こちらの記事を先に読んでいただくことをお勧めします。

利用するAPIとAPI引数

メッセージを取得するためにはこちらのAPIを使います。
投稿時間を指定するためには以下のoldest, latest引数を設定してAPIを呼び出す必要があります。

image-20200709090717712.png

oldestは取得するメッセージの投稿時間の最小値、latestは取得するメッセージの投稿時間の最大値を表します。つまり、oldestとlatestを両方設定すると、oldest~latest間のメッセージを取得することができます。

oldest, latestの指定方法

こちらの公式文書に書かれているようにSlackでは時間をUNIX時間形式で扱っています。UNIX時間はUNIX系のコンピューターシステムで標準的に使われている時刻表現のことです。詳細はこちらを参照ください。
そのため、例えば2020年7月1日に投稿されたメッセージを取得する場合は、oldest, latestにそれぞれ以下のような値を入れて、APIを呼び出します。

  • oldest
    2020年7月1日 0:00を表現するUNIX時間

  • latest
    2020年7月2日 0:00を表現するUNIX時間

C#でUNIX時間を作り出す方法

C#上でUNIX時間を作り出すには、DateTimeOffset構造体から変換するのが簡単だと思います。
手順としては、以下になります。

  1. DateTime構造体でUNIX時間に変換したい日時情報を作成
  2. 1でつくったDateTime型の値を使って、DateTimeOffset構造体を作成(リファレンスはこちら)
  3. 2のDateTimeOffset型の値をこちらのメソッドでUNIX時間に変換

時間を指定してメッセージを取得するサービス

これまでの内容を活かして、投稿時間を指定してメッセージを取得するサービスを作成します。こちらの記事で作成したメッセージを取得するGetMessagesメソッドを拡張して、実現します。

/// <summary>
/// Slackメッセージの取得に必要な機能を提供するサービス
/// </summary>
public class SlackMessageGetService
{
    #region 定数

    /// <summary>
    /// アクセストークン用のクエリパラメータ名
    /// </summary>
    private const string c_AccessTokenQueryParameterName = "token";

    /// <summary>
    /// チャンネルID用のクエリパラメータ名
    /// </summary>
    private const string c_ChannelIdQueryParameterName = "channel";

    /// <summary>
    /// 取得メッセージ数用のクエリパラメータ名
    /// </summary>
    private const string c_MessageCountQueryParameterName = "limit";

    /// <summary>
    /// 投稿時間の最小値用のクエリパラメータ名
    /// </summary>
    private const string c_OldestQueryParameterName = "oldest";

    /// <summary>
    /// 投稿時間の最大値用のクエリパラメータ名
    /// </summary>
    private const string c_LatestQueryParameterName = "latest";

    ・・・

    #endregion

    #region フィールド

    /// <summary>
    /// SlackにPOST,GETするために使用するHttpClient
    ///
    /// ユーザーの利用方法を想定したとき、接続先のホストは[チーム名].slack.comしかありえないため、
    /// HttpClientは単一インスタンスのみとする
    /// </summary>
    // ReSharper disable once InconsistentNaming
    private static readonly HttpClient m_HttpClient = new HttpClient();

    #endregion

    #region 公開サービス

    /// <summary>
    /// メッセージを取得する
    /// </summary>
    /// <param name="accessToken">アクセストークン</param>
    /// <param name="channelId">チャンネルID</param>
    /// <param name="messageCount">取得するメッセージ数</param>
    /// <param name="oldest">投稿時間の最小値</param>
    /// <param name="latest">投稿時間の最大値</param>
    /// <returns>取得メッセージ一覧</returns>
    public async Task<IEnumerable<Message>> GetMessages(string accessToken, string channelId, int messageCount, DateTime oldest, DateTime latest)
    {
        var messages = new List<Message>();

        // latestとoldestをUNIX時間に変換
        var oldestDateTimeOffset = new DateTimeOffset(oldest);
        var oldestUnixTime = oldestDateTimeOffset.ToUnixTimeSeconds();
        var latestDateTimeOffset = new DateTimeOffset(latest);
        var latestUnixTime = latestDateTimeOffset.ToUnixTimeSeconds();

        // クエリパラメータを作成するためにディクショナリを作成
        // ディクショナリのKeyがクエリパラメータ名、ディクショナリのValueがクエリパラメータの値
        var parameters = new Dictionary<string, string>()
        {
            { c_AccessTokenQueryParameterName, accessToken},
            { c_ChannelIdQueryParameterName, channelId},
            { c_MessageCountQueryParameterName, messageCount.ToString()},
            { c_OldestQueryParameterName, oldestUnixTime.ToString()},
            { c_LatestQueryParameterName, latestUnixTime.ToString()}
        };

        try
        {
            // クエリパラメータを作成し、文字列で読み出す
            var parametersString = await new FormUrlEncodedContent(parameters).ReadAsStringAsync();
            // 読みだしたクエリパラメータを使ってリクエストURLを作成する。
            var requestBaseUrl = "https://slack.com/api/conversations.history";
            var requestUrl = $"{requestBaseUrl}?{parametersString}";
            var response = await m_HttpClient.GetAsync(requestUrl).ConfigureAwait(false);
            // レスポンスのコンテンツをstringで読み出す
            var responseBodyString = await response.Content.ReadAsStringAsync();
            // 読みだしたJsonを、オブジェクトにデシリアライズする
            var responceObject = JsonConvert.DeserializeObject<GetSlackMessagesResponce>(responseBodyString);

            // 戻り値用のメッセージ一覧を作成
            foreach (var messageResponce in responceObject.messages)
            {
                // 本文を設定
                var message = new Message()
                {
                    Text = messageResponce.text
                };

                // 添付ファイルを設定
                foreach (var file in messageResponce.files)
                {
                    var addFile = new File()
                    {
                        FileURL = file.url_private,
                        Id = file.id,
                    };
                    message.Files.Add(addFile);
                }

                messages.Add(message);
            }

            return messages;
        }
        catch
        {
            return messages;
        }
    }

    ・・・

    #endregion
}

メソッドに新たに追加したロジックである、引数oldestとlatestをUNIX時間に変換する箇所を細かく説明します。
引数oldestとlatestはDateTime型なので、その値を使ってDateTimeOffset構造体の値を作ります。コンストラクタにDataTime型の値を渡すだけで作成できます。その後、ToUnixTimeSecondsメソッドでUNIX時間に変換します。
その後は、元々API引数として渡していたchannelIdなどと同様にディクショナリに情報を詰めます。ディクショナリのKey値がAPI引数名(oldestやlatest)、Value値が実際の値です。ディクショナリをどのようにAPI引数とするかの細かい説明は以前の記事を参照ください。

これらのロジック追加によって、oldest,latestを指定してAPIを呼び出すことが可能になりました。

まとめ

本記事では、C#からSlackAPIを利用して指定期間内に投稿されたメッセージを取得する方法をまとめました。
最も伝えたかったのは、以下2点です。

  • SlackAPIにおいて時間はUNIX時間で表現される
  • C#でUNIX時間を作るにはDateTimeOffset構造体を使うのが簡単
  • このエントリーをはてなブックマークに追加
  • Qiitaで続きを読む

Windows Formで沢山あるラジオボタンの任意の一つを選択状態にするド素人的解決

最近C#の勉強をしなければならなくなって,以前にhtaで書いたソフトウェアをC#+Windows Formで作り直すってやってんだけど,「コードで複数のラジオボタンの一つをチェック状態にする」ってのがもう全然判らなくて2日ほど嵌ってしまった。
いやまあラジオボタンにチェック付けるだけなら

ラジオボタン名.Checked = true

でええんやけど,そうではなく.
例えば10個あるラジオボタンのn番目にチェックを付けたいって時のやり方がGoogleっても全然出てこないのよ.
最初は

グループ名.Controls[n].Checked = true

でいけるやろ? と思ったんだけど,Controls[n]のプロパティに「Checked」が無いでやんの(´・ω・`).
まあControlsに含まれるのはラジオボタンとは限らんもんな……
じゃあOfTypeでラジオボタンだけフィルタすればよくない?と思いついて

グループ名.Controls.OfType<RadioButton>()[n].Checked = true

ってやってみるも「角かっこ[]付きのインデックスを'IEnumerable'型の式に適用することはできません」ってエラー.なんでや!
そして,2日間いろいろ試行錯誤してようやくたどり着いたのがこれ↓

グループ名.Controls.OfType<RadioButton>().ToArray()[n].Checked = true

OfTypeでラジオボタンだけフィルタしてToArrayで配列に変換した後に[n]で当該ラジオボタンを選択.これだとCheckedが使えるぞ!!!
もうちょっと簡単な方法ありそうだけどまあ動くのでこれで.

※ちないろいろGoogleってた時にラジオボタン名を連番にして名前で指定,みたいのんは見かけたんだけど,実際には上記グループはタブになってて「1番目のタブの2番目のラジオボタン」みたいな指定しなきゃならんのでラジオボタン名を生成するのが面倒くさくてスルーやで.

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

ASP.NET Core MVC 3.1 入門 その4 「Routing」

はじめに

ASP.NET Core MVC 3.1 の Routing について、自分が学んだことを備忘録として記載します。
このフレームワークに殆ど触れたことが無い方に少しでも参考になれば幸いです。
誤り等あれば、ご指摘頂けますと大変喜びます。

前回の記事

ASP.NET Core MVC 3.1 入門 その3 「Controller」

今回の流れ

  • 挙動の確認
  • Routingの説明
  • Routeの説明
  • Routeの定義
  • デフォルトのRouteを変更してみる

今回のゴール

  • Routingとは何かを知る
  • https://localhost:XXXXX/Hello/Indexとすると、なぜHelloControllerIndexアクションメソッドが呼び出されるのかを理解する
  • デフォルトのRouteを変更できるようになる

環境

IDE
Visual Studio 2019
言語
C#

前提

以下2つのコントローラークラスが存在するものとします。
いずれもテキストデータを返すだけのIndexアクションメソッドが定義されています。
アクションメソッドについては、前回の記事を参照してください。

    public class HelloController : Controller
    {
        public IActionResult Index()
        {
            return Content("Hello World!");
        }
    }
    public class HomeController : Controller
    {
        public IActionResult Index()
        {
            return Content("Home");
        }
    }

StartUpクラスはテンプレートのままです。

    public class Startup
    {
        //省略

        public void Configure(IApplicationBuilder app, IWebHostEnvironment env)
        {
            //省略

            app.UseRouting();

            //省略

            app.UseEndpoints(endpoints =>
            {
                endpoints.MapControllerRoute(
                    name: "default",
                    pattern: "{controller=Home}/{action=Index}/{id?}");
            });
        }
    }

挙動を確認する

実行してみると、以下のようにHomeControllerIndexアクションメソッドが呼び出されています。理由は後述します。

image.png

ここでアドレスバーから手入力でURLを変更します。
変更前は以下のようになっているかと思います。
XXXXXの部分はポート番号を示しています。
環境とタイミング次第で異なりますので、ご自身の画面に表示されているものから変更する必要はありません。

https://localhost:XXXXX/

以下のようにアドレスバーから手入力でURLを変更してください。
末尾にHello/Indexを追加します。

https://localhost:XXXXX/Hello/Index

変更後、Enterを押下すると、以下のように「Hello World!」が表示されると思います。
これはつまり、HelloControllerIndexアクションメソッドが呼び出されています。

image.png

ここで生じた2つの疑問について、仕組みを説明していきます。

  • 実行すると、なぜHomeControllerIndexアクションメソッドが呼び出されるのか
  • Hello/Indexとすると、なぜHelloControllerIndexアクションメソッドが呼び出されるのか

Routing

クライアントから要求されたURLに応じて、呼び出すコントローラーやアクションを決定する仕組みのことです。
ASP.NET Core MVC では、クライアントからの要求を受け取ると、まずは、Routingを利用して、呼び出すべきコントローラー(アクションメソッド)を決定します。

Routingの流れ

  1. リクエストのURLをアプリケーションが定義しているルート(Route)と照合します。
  2. 一致するルートが見つかった場合は、該当するコントローラーのアクションメソッドが呼び出されます。
    ※見つからなければ、404(NotFound)エラーとなります。
https://localhost:XXXXX/Hello/Index

従ってHelloControllerIndexアクションメソッドが呼び出されるのは、上記のURLをルートと照合した結果、一致するルートが見つかったからということになります。

ルート(Route)

では、ルートとはどこでどのように定義されているのでしょうか。

ルートとは、URLからどのコントローラーに処理を関連づけるかのパターンマッチング文字列です。
プロジェクト直下のStartup.csConfigureメソッド)で定義されています。

Configureはアプリケーションを起動する際に呼び出されるメソッドで、アプリの基本設定を宣言する場所ですね。

    public class Startup
    {
        //省略

        public void Configure(IApplicationBuilder app, IWebHostEnvironment env)
        {
            //省略

            app.UseRouting();

            //省略

            app.UseEndpoints(endpoints =>
            {
                endpoints.MapControllerRoute(
                    name: "default",
                    pattern: "{controller=Hello}/{action=Index}/{id?}");
            });
        }
    }

UseRoutingメソッドによって、Routingを有効化しています。
有効化されていない状態で実際の振り分け先を決めるUseEndpointsメソッドを呼び出すとエラーになります。

ルートを定義しているのは、MapControllerRouteメソッドです。

    app.UseEndpoints(endpoints =>
    {
        endpoints.MapControllerRoute(
            name: "default",
            pattern: "{controller=Home}/{action=Index}/{id?}");
    });

MapControllerRouteメソッド

    endpoints.MapControllerRoute(
        name: "default",
        pattern: "{controller=Home}/{action=Index}/{id?}");
  • name
    defaultの部分
    設定を識別するための名前です。
    一意でさえあれば自由に設定可能です。
    この時点では、複数設定できるということだけ抑えておけば問題ありません。

  • pattern
    {controller=Home}/{action=Index}/{id?}の部分
    ルーティングの際にマッチングをおこなうためのURLパターンを示す文字列です。

pattern

{controller=Home}/{action=Index}/{id?}とありますが

まずはシンプルに
{controller}/{action}/{id}と解釈してください。

{XXX}はプレースホルダーです。
controlleractionは予約されている名前で、それぞれコントローラー名と
アクションメソッド名と紐づきます。
{id}にはアクションメソッドに渡される任意のパラメーターを設定することができます。

従って~/Home/Index/5であればHomeControllerIndexアクションメソッドを呼び出し、パラメーターとして5を渡す という意味になります。

patternの省略(デフォルト値)

では、説明を飛ばした=Homeidに付与されている?の意味も確認しておきましょう。

デフォルト値

省略された場合のデフォルト値を用意しておきたいならば、{controller=Home}のように書きます。(「=」以降がデフォルト値です)
上記の通り設定していた場合http://localhost:XXXXXとすると、コントローラー名とアクション名が省略されているため、デフォルト値が効いて、HomeControllerIndexアクションメソッドが呼び出されることになります。

  • コントローラー名とアクション名を省略してみる image.png

省略可能なパラメーター

{id?}の「?」は、そのパラメーターが省略できることを示しています。
{controller}/{action}/{id?}であれば、Home/Index/5のようなパスだけでなく、Home/Indexでもマッチします。

  • idを省略してみる image.png

デフォルトのルートを変更してみる

MapControllerRouteメソッドを以下のように修正します。

    app.UseEndpoints(endpoints =>
    {
        endpoints.MapControllerRoute(
            name: "default",
            pattern: "{controller=Hello}/{action=Index}/{id?}"); //Home => Hello
    });

実行すると、コントローラー名とアクション名が省略されているため、デフォルト値が効いて、HelloControllerIndexアクションメソッドが呼び出されています

image.png

以上となります。
ありがとうございました。

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

【C#】ジェネリック

★ジェネリックとは
・様々な型に対応できるようにパラメータとして与え、オブジェクトを定義できるようにした機能

◇ジェネリッククラス
特定のデータ型の引数の差異を一元化してコードを簡略化することが可能

ジェネリッククラスの記述方法
//型に依存していない場合
public class SampleJeneric<VAR>
{
    public VAR Test(VAR a1){ return a1; }
}

//型に依存しているメソッドを呼び出す場合
public class SampleJeneric<VAR>
    where VAR : struct
{
    public VAR Test(VAR a1){ return a1; }
}


◇ジェネリックメソッド
・様々なデータに対応したメソッド
・引数の数やデータ型、戻り値の型を指定

ジェネリックメソッドの記述方法
int Min<T>(int a, int y)
 where int : IComparable
{
    return a.ComparaTo(b) < 10 ? a : b;
}

★ジェネリック型制約
・ジェネリックメソッドの型引数が持つ機能を指定し、対応できるようにします。
・各製薬は併用指定可能

ジェネリック型制約の種類

種類 内容
where T:struct 値型のみ
値型と参照型を参照
他制約と併用する場合は最初に記述
where T:class 参照型のみ
他制約と併用する場合は最初に記述
where T:クラス名 指定したクラス、またはその派生クラス
where T:インターフェース名 指定したインターフェイスクラスを継承するクラス
where T:new() 引数無しのコンストラクターを持つクラス
他制約と併用する場合は最初に記述
where T:unmanaged アンマネージ型
値型をポインタで扱う場合に使用
  • このエントリーをはてなブックマークに追加
  • Qiitaで続きを読む

【C#】タグ一覧

C#のコード記述の際に使用されるタグの一覧

タグ名 内容
summary 型また型のメンバの概要
remarks 追記(追加情報)
value プロパティ値説明
para 記タグ内で段落作成が可能
summary・remarks・returns
param メソッドパラメータ、型自動判定
typeparam ジェネリック型1パラメータ、型自動判定
paramref インラインのパラメータ参照
typeparamref ジェネリック型1パラメータ参照
returns 戻り値
exception 例外
タグの記述方法
/// <summary>概要の説明</summary>
/// <remarks>備考</remarks>
/// <value>プロパティ値説明</value>
/// <para>段落テキスト</para>
/// <param name="メソッド パラメータ名">パラメータの説明</param>
/// <paramref name="参照パラメータ名" />
/// <typeparam name="型パラメータ名>型パラメータの説明</typeparam>
/// <typeparamref name="参照パラメータ名" />
/// <returns>戻り値の説明</returns>
/// <exception cref="member">例外説明</>
  • このエントリーをはてなブックマークに追加
  • Qiitaで続きを読む

【C#】XMLドキュメントコメントのタグ一覧

C#のXMLドキュメントコメント記述の際に使用されるタグの一覧

タグ名 内容
summary 型また型のメンバの概要
remarks 追記(追加情報)
value プロパティ値説明
para 記タグ内で段落作成が可能
summary・remarks・returns
param メソッドパラメータ、型自動判定
typeparam ジェネリック型1パラメータ、型自動判定
paramref インラインのパラメータ参照
typeparamref ジェネリック型1パラメータ参照
returns 戻り値
exception 例外
タグの記述方法
/// <summary>概要の説明</summary>
/// <remarks>備考</remarks>
/// <value>プロパティ値説明</value>
/// <para>段落テキスト</para>
/// <param name="メソッド パラメータ名">パラメータの説明</param>
/// <paramref name="参照パラメータ名" />
/// <typeparam name="型パラメータ名>型パラメータの説明</typeparam>
/// <typeparamref name="参照パラメータ名" />
/// <returns>戻り値の説明</returns>
/// <exception cref="member">例外説明</>
  • このエントリーをはてなブックマークに追加
  • Qiitaで続きを読む

[WinForm] USBカメラの列挙

var searcher = new ManagementObjectSearcher(@"SELECT * FROM Win32_PnPSignedDriver WHERE DeviceID LIKE 'USB%' AND DeviceClass = 'Camera'");

foreach (var device in searcher.Get().Cast<ManagementObject>().OrderBy(n => n["PDO"]))
{
    Debug.WriteLine("-----");
    Debug.WriteLine(device.GetPropertyValue("FriendlyName"));
    Debug.WriteLine(device.GetPropertyValue("DeviceClass"));
    Debug.WriteLine(device.GetPropertyValue("DeviceID"));
    Debug.WriteLine(device.GetPropertyValue("PDO"));
    Debug.WriteLine("-----");
}

※「Video Capture Device ID」(index)は、PDOが小さい順に振られると思っていますが、完全には調べ切れていません...

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