- 投稿日:2020-07-14T17:05:15+09:00
[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["飛車, 角, 金, 銀, 桂馬, 香車"]参考
- 投稿日:2020-07-14T16:39:17+09:00
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
を読み込むためです。一部の特殊なテンプレートについては、
Views
またはViews/Shared
に配置します。特殊なテンプレートについては、後述します。Viewスクリプト
Viewテンプレート内に記述するスクリプトです。
Viewエンジン固有の言語(Razor)で記述していきます。Viewを追加する
では実際にViewを作ってみましょう。
ASP.NET Core MVC 3.1 のプロジェクトを作成する
まずは「Web アプリケーション(モデル ビュー コントローラー)」テンプレートでプロジェクトを作成します。
ソリューションエクスプローラーを眺める
各フォルダ/ファイルの概要についてはこちらの記事をご参照ください。
前述したとおり、Viewテンプレートは/Views/コントローラー名/アクションメソッド名.cshtml
に配置するようにしましょう。Controller の追加
HelloController
を追加します。
Controller
についてはこちらの記事をご参照ください。public class HelloController : Controller { public IActionResult Index() { return View(); } }View の追加
新しい View を追加してみます。
アクションメソッド(Indexメソッド)のメソッド名を右クリック > ビューの追加
と進んでください。
スキャフォールディング
以下のようなウィンドウが立ち上がると思います。
「スキャフォールディング」というのは、要は雛形を自動生成する機能のことを示します。
今回はViewの雛形を自動生成してもらうために、この機能を使います。自動で
/Views/コントローラー名/
に配置してくれたり、指定したモデルクラスの新規作成(Create
)や一覧表示(List
)のテンプレート(Bootstrap向けのcssセレクタまで付与されたもの)を用意してくれたりと、手間が省けるので、 Viewを追加する際には、この手順でスキャフォールディング機能を利用することをおすすめします。今回は「Razor ビュー」> 「Empty(モデルなし)」を選択します。
上述した通り、よりリッチなテンプレートもありますが、まずはシンプルなViewを使っていきましょう。続いてビュー名ですが、
アクションメソッド名
とします。
アクションメソッドで呼び出されるView
メソッドが
明示的に指定しない限り、/Views/コントローラー名/アクションメソッド名.cshtml
を読み込むためです。無事に
Hello
コントローラーのIndex
アクションメソッドと対応する View(Index.cshtml) を追加することができました。Viewのコードを確認する
以上でViewは追加できたと思います。
では、追加されたViewを見ていきましょう。Index.cshtml
まず注目して頂きたいのは
<h1>
タグです。
Viewスクリプトには、お馴染みの HTML/CSS/Javascript をゴリゴリ書いていくことができます。
HTML/CSS/Javascript を用いてページの構成を作りつつ、C#のコードを利用して動的な値を埋め込んでいくというのがView開発の流れになります。@{ ViewData["Title"] = "Index"; } <h1>Index</h1>次に
@
やViewData
といった見慣れない構文が目に付くかと思います。これらの構文については後述します。説明の便宜上、
HelloController
とIndex.cshml
に少々手を加えます。まずは
HelloController
のIndex
メソッドを以下のように修正します。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.cs
のConfigure
メソッドを以下のように修正し、HelloController
のIndex
アクションメソッドが呼び出されるように、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
アクションメソッドで設定した値が画面に表示されれば成功です。View変数(ViewBag/ViewData)
<h2>@ViewBag.Message</h2> <p>@ViewData["Now"]</p>
ViewBag
とViewData
をView変数と呼びます。
要はViewスクリプトに埋め込む値のことです。
Controller のアクションメソッドから View にデータを渡すことができます。Controller側で表示に必要なデータを用意しておき、Viewではデータを埋め込む場所や表示方法を定義する
というのが Controller と View の基本的な関係となります。
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
を使うと、ViewData
のDictionary
ベースの読みづらいコードを記述する必要はなくなりますが、DLRによって解釈されるコードを使いたくないのであればViewData
をおすすめします。多用は禁物
View変数の利用は、 Controller から View にデータを渡す最も単純な方法です。
ただし、多用は禁物です。
型の指定ができないので、例えば Controller と View を別の開発者が担当していた場合、 View 側の開発者からすれば、一体どんなデータが設定されてくるのか、見当がつかないかもしれません。そんな時は、後々記事としてあげますが、強く型指定された
ViewModel
を利用すべきと筆者は考えます。@(コードナゲット)
続いて、
@
について説明していきます。
Viewスクリプトにおいて、C#のコードであることを示すキーワードです。一行の場合、明示的に閉じる必要はありません。(セミコロンは不要です)コード行がどこで閉じるか解析してくれます。
<h2>@ViewBag.Message</h2> <p>@ViewData["Now"]</p>コードブロックを記述したいときは
@{...}
とします。@{ ViewData["Title"] = "Index"; }特殊なViewテンプレート
特定のアクションメソッドと対応しない特殊なViewが存在します。
ここでは最も基本的な2つを紹介します。レイアウトページ(_Layout.cshtml)
まずは動作確認
再度実行してみましょう。
以下のような画面が表示されると思います。少々違和感があります。
Index.cshtml
に記述したコードはこれだけです。
にも関わらず、それっぽいヘッダーやフッターが付いていますね。@{ ViewData["Title"] = "Index"; } <h1>Index</h1> <h2>@ViewBag.Message</h2> <p>@ViewData["Now"]</p>これはレイアウトページ(
_Layout.cshtml
)という仕組みによるものです。
複数のページで共通するデザインの外枠を定義するための仕組みです。
レイアウトページを指定しておくと、ヘッダー、フッター、サイドメニューといった共通のデザインを一か所にまとめることができます。変更する際も、レイアウトページだけを変更すれば一気に適用されるので、保守性も高まります。
レイアウトページは
Views/Shared/
に格納されています。
レイアウトページだけではなく、複数のViewテンプレートから利用し得るものは基本ここに配置します。
_Layout.cshtml
のコードを確認しましょう。
header
とfooter
の間に以下のコードが見つかると思います。@*省略*@ <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の処理に当たって、最初に処理される特別なファイルです。重要なのはレイアウトページと異なり、名前空間もファイル名も固定です。
役割
一般的にはViewで使用するレイアウトページを指定するコードを記述します。実際にテンプレートでも
_Layout
をレイアウトページとして扱う旨のコードが記述されていることが確認できます。ただし、個別のViewでレイアウトページを改めて指定した場合はそちらが優先されます。
@{ Layout = "_Layout"; }これが、ただのViewテンプレートの1つに過ぎない
_Layout.cshtml
がレイアウトページとして検出されている理由です。_ViewStart.cshtml
で指定されていたというわけです。他にも、View関連のスタートアップコードが必要な場合はここに記述します。
規約
ファイル名
_ViewStart.cshtml
配置場所(名前空間)
Views/
直下以上です。
ありがとうございました。
- 投稿日:2020-07-14T16:17:21+09:00
NugetのCoreTweetパッケージを導入する
はじめに
C#アプリでTweet連携をするためのNuGetパッケージを導入してみることにした。
CoreTweetとは
OSSのTwitterライブラリ。2020年7月現在にて、使えるライブラリとされる。
環境
項目 設定値 OS Windows 10 開発環境 Visual Studio Community 2019 version: 16.6.3 1. coreTweetパッケージのインストール
参考
- CoreTweet API ドキュメント http://coretweet.github.io/docs/index.html
- 作って覚えるVisual C# 2017 デスクトップアプリ入門
- 投稿日:2020-07-14T13:43:30+09:00
C#からSlackAPIを利用して、指定期間内に投稿されたメッセージを取得する
はじめに
本記事はC#からSlackAPIを利用して、指定期間内に投稿されたメッセージを取得する方法をまとめることを目的としています。
なお、以前に以下のようなSlackAPIに関する記事を投稿していますので、こちらの記事を先に読んでいただくことをお勧めします。
SlackAPIを使うための準備
https://qiita.com/tat_tt/items/e10bd5d9aee897f0b8b6メッセージを画像付きで取得する方法
https://qiita.com/tat_tt/items/14b688ea48afbcd52505利用するAPIとAPI引数
メッセージを取得するためにはこちらのAPIを使います。
投稿時間を指定するためには以下のoldest, latest引数を設定してAPIを呼び出す必要があります。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構造体から変換するのが簡単だと思います。
手順としては、以下になります。
- DateTime構造体でUNIX時間に変換したい日時情報を作成
- 1でつくったDateTime型の値を使って、DateTimeOffset構造体を作成(リファレンスはこちら)
- 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構造体を使うのが簡単
- 投稿日:2020-07-14T13:21:16+09:00
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 = trueOfTypeでラジオボタンだけフィルタしてToArrayで配列に変換した後に[n]で当該ラジオボタンを選択.これだとCheckedが使えるぞ!!!
もうちょっと簡単な方法ありそうだけどまあ動くのでこれで.※ちないろいろGoogleってた時にラジオボタン名を連番にして名前で指定,みたいのんは見かけたんだけど,実際には上記グループはタブになってて「1番目のタブの2番目のラジオボタン」みたいな指定しなきゃならんのでラジオボタン名を生成するのが面倒くさくてスルーやで.
- 投稿日:2020-07-14T12:08:08+09:00
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
とすると、なぜHelloController
のIndex
アクションメソッドが呼び出されるのかを理解する- デフォルトの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?}"); }); } }挙動を確認する
実行してみると、以下のように
HomeController
のIndex
アクションメソッドが呼び出されています。理由は後述します。ここでアドレスバーから手入力でURLを変更します。
変更前は以下のようになっているかと思います。
XXXXX
の部分はポート番号を示しています。
環境とタイミング次第で異なりますので、ご自身の画面に表示されているものから変更する必要はありません。https://localhost:XXXXX/以下のようにアドレスバーから手入力でURLを変更してください。
末尾にHello/Index
を追加します。https://localhost:XXXXX/Hello/Index変更後、Enterを押下すると、以下のように「Hello World!」が表示されると思います。
これはつまり、HelloController
のIndex
アクションメソッドが呼び出されています。ここで生じた2つの疑問について、仕組みを説明していきます。
- 実行すると、なぜ
HomeController
のIndex
アクションメソッドが呼び出されるのかHello/Index
とすると、なぜHelloController
のIndex
アクションメソッドが呼び出されるのかRouting
クライアントから要求されたURLに応じて、呼び出すコントローラーやアクションを決定する仕組みのことです。
ASP.NET Core MVC では、クライアントからの要求を受け取ると、まずは、Routingを利用して、呼び出すべきコントローラー(アクションメソッド)を決定します。Routingの流れ
- リクエストのURLをアプリケーションが定義しているルート(Route)と照合します。
- 一致するルートが見つかった場合は、該当するコントローラーのアクションメソッドが呼び出されます。
※見つからなければ、404(NotFound)エラーとなります。https://localhost:XXXXX/Hello/Index従って
HelloController
のIndex
アクションメソッドが呼び出されるのは、上記のURLをルートと照合した結果、一致するルートが見つかったからということになります。ルート(Route)
では、ルートとはどこでどのように定義されているのでしょうか。
ルートとは、URLからどのコントローラーに処理を関連づけるかのパターンマッチング文字列です。
プロジェクト直下のStartup.cs
(Configure
メソッド)で定義されています。
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}
はプレースホルダーです。
controller
とaction
は予約されている名前で、それぞれコントローラー名と
アクションメソッド名と紐づきます。
{id}
にはアクションメソッドに渡される任意のパラメーターを設定することができます。従って
~/Home/Index/5
であればHomeController
のIndex
アクションメソッドを呼び出し、パラメーターとして5
を渡す という意味になります。patternの省略(デフォルト値)
では、説明を飛ばした
=Home
やid
に付与されている?
の意味も確認しておきましょう。デフォルト値
省略された場合のデフォルト値を用意しておきたいならば、
{controller=Home}
のように書きます。(「=
」以降がデフォルト値です)
上記の通り設定していた場合http://localhost:XXXXX
とすると、コントローラー名とアクション名が省略されているため、デフォルト値が効いて、HomeController
のIndex
アクションメソッドが呼び出されることになります。省略可能なパラメーター
{id?}
の「?
」は、そのパラメーターが省略できることを示しています。
{controller}/{action}/{id?}
であれば、Home/Index/5
のようなパスだけでなく、Home/Index
でもマッチします。デフォルトのルートを変更してみる
MapControllerRoute
メソッドを以下のように修正します。app.UseEndpoints(endpoints => { endpoints.MapControllerRoute( name: "default", pattern: "{controller=Hello}/{action=Index}/{id?}"); //Home => Hello });実行すると、コントローラー名とアクション名が省略されているため、デフォルト値が効いて、
HelloController
のIndex
アクションメソッドが呼び出されています以上となります。
ありがとうございました。
- 投稿日:2020-07-14T10:32:23+09:00
【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 アンマネージ型
値型をポインタで扱う場合に使用
- 投稿日:2020-07-14T09:56:01+09:00
【C#】タグ一覧
C#のコード記述の際に使用されるタグの一覧
タグ名 内容 summary 型また型のメンバの概要 remarks 追記(追加情報) value プロパティ値説明 para 記タグ内で段落作成が可能
summary・remarks・returnsparam メソッドパラメータ、型自動判定 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">例外説明</>
- 投稿日:2020-07-14T09:56:01+09:00
【C#】XMLドキュメントコメントのタグ一覧
C#のXMLドキュメントコメント記述の際に使用されるタグの一覧
タグ名 内容 summary 型また型のメンバの概要 remarks 追記(追加情報) value プロパティ値説明 para 記タグ内で段落作成が可能
summary・remarks・returnsparam メソッドパラメータ、型自動判定 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">例外説明</>
- 投稿日:2020-07-14T09:09:13+09:00
[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が小さい順に振られると思っていますが、完全には調べ切れていません...