- 投稿日:2019-10-04T23:19:17+09:00
Azure Notification Hubを使ってC#でiOSアプリにプッシュ通知を送信
はじめに
Azure Notification Hubを使ってC#でiOSアプリにタグ指定でプッシュ通知を送信するメモ。
Azure Notification Hubを使うと、任意のデバイスに任意のタグ指定(スマホアプリ側で指定した任意の文字列)で
送信することができるので実装が楽。環境
・Azure Notification Hub
・Windows 10 Pro
・C#(Visual Studio 2019)
・.NET Core 2.1
・コンソールアプリケーション
・使用したデバイス iPad Air事前準備
1.Azure Notification Hubの構築
APNSの設定等あるので詳細はMicrosoftのドキュメントをご確認ください。
作成時に以下の情報をメモ
・Notification Hubの名前
-> Overview/Name
・Notification Hubの接続文字列
-> Manage/Access Policiesの送信権限が付与されているPolicyのConnection String
(デフォルトだと:DefaultFullSharedAccessSignatureのほう)2.スマホにテスト用アプリをインストール
・接続時に指定したTagをメモ3.NuGetで使用するパッケージをインストール
Visual Studioでプロジェクトを開き
NuGetパッケージマネージャで以下のパッケージを検索しインストール
・Microsoft.Azure.NotificationHubs(作成者:Microsoft)実装
Program.csusing Microsoft.Azure.NotificationHubs; using System; namespace Test { class Program { static void Main(string[] args) { string connectionString = "Azureに作成したNotificationHubのConnection String"; string hubName = "送信対象のハブ名"; string payload = @"{""aps"":{""alert"":""test""}}"; string tag = "送信対象のタグ"; // クライアントを作成 var client = NotificationHubClient.CreateClientFromConnectionString(connectionString, hubName); // iOSにタグ指定でJsonデータを送信 var task = client.SendAppleNativeNotificationAsync(payload, tag); // AndroidもSendFcmNativeNotificationAsyncを使うことで簡単にできそうだが未確認 // ~以下略~ } } }
- 投稿日:2019-10-04T20:27:34+09:00
C#やPowerShellで画面上の特定の画像の位置をクリックする方法
まえがき
以前、こんな記事を書いたことがあります。
色々な方法でWindowsのGUIの自動操作を行う方法を記載しましたが、PowerShellで画像認識を利用した自動操作については逃げました。今回は宿題として残っていたPowerShellとOpenCVを使用して画像認識での自動操作を行ってみます。
考え方としてはスクリーンキャプチャした内容をMatに変換してTemplate Matchingを行うだけです。
OpenCVの.NET用のラッパー
OpenCVには.NET用のラッパーとしてOpenCvSharpが存在します。
https://github.com/shimat/opencvsharp/releasesこのライブラリをNugetまたは上記のページからダウンロードしてください。
注意点として、ネイティブのDLLを使うことになるので32bit、64bitのどちらのプロセスで動作しているか、意識してDLLを利用してください。C#のサンプル
VisualStudio 2019の.NET Framework4.0で作成したサンプルは以下のようになります。
using OpenCvSharp; using System; using System.Collections.Generic; using System.Drawing; using System.Runtime.InteropServices; using System.Windows.Forms; namespace OpenCv { public class GuiAuto { // https://culage.hatenablog.com/entry/20130611/1370876400 [DllImport("user32.dll")] extern static uint SendInput(uint nInputs, INPUT[] pInputs, int cbSize); [StructLayout(LayoutKind.Sequential)] struct INPUT { public int type; public MOUSEINPUT mi; } [StructLayout(LayoutKind.Sequential)] struct MOUSEINPUT { public int dx; public int dy; public int mouseData; public int dwFlags; public int time; public IntPtr dwExtraInfo; } const int MOUSEEVENTF_LEFTDOWN = 0x0002; const int MOUSEEVENTF_LEFTUP = 0x0004; static public void Click() { //struct 配列の宣言 INPUT[] input = new INPUT[2]; //左ボタン Down input[0].mi.dwFlags = MOUSEEVENTF_LEFTDOWN; //左ボタン Up input[1].mi.dwFlags = MOUSEEVENTF_LEFTUP; //イベントの一括生成 SendInput(2, input, Marshal.SizeOf(input[0])); } static public void Move(int x, int y) { var pt = new System.Drawing.Point(x, y); System.Windows.Forms.Cursor.Position = pt; } public class TemplateResult { public int TargetWidth { set; get; } public int TargetHeight { set; get; } public List<OpenCvSharp.Point> MatchList { set; get; } public TemplateResult() { this.MatchList = new List<OpenCvSharp.Point>(); } } static public TemplateResult MatchTemplate(int ScreenNo, string targetPath, double threshold) { TemplateResult result = new TemplateResult(); var screen = Screen.AllScreens[ScreenNo]; Bitmap bitmap = new Bitmap(screen.Bounds.Width, screen.Bounds.Height); Graphics graphics = Graphics.FromImage(bitmap as Image); graphics.CopyFromScreen(screen.Bounds.X, screen.Bounds.Y, 0, 0, bitmap.Size); using (var targetImg = Cv2.ImRead(targetPath)) using (var img = OpenCvSharp.Extensions.BitmapConverter.ToMat(bitmap)) using (var img3ch = img.CvtColor(ColorConversionCodes.BGRA2BGR)) { result.TargetWidth = targetImg.Width; result.TargetHeight = targetImg.Height; var tmplRet = img3ch.MatchTemplate(targetImg, TemplateMatchModes.CCoeffNormed); double minVal, maxVal; OpenCvSharp.Point minLoc, maxLoc; tmplRet.MinMaxLoc(out minVal, out maxVal, out minLoc, out maxLoc); Mat thresholdRet = tmplRet.Threshold(threshold, 1.0, ThresholdTypes.Tozero); while (true) { thresholdRet.MinMaxLoc(out minVal, out maxVal, out minLoc, out maxLoc); if (maxVal < threshold) { break; } result.MatchList.Add(maxLoc); thresholdRet.FloodFill(maxLoc, 0); } } return result; } static public bool ClickImg(int ScreenNo, string targetPath, double threshold, int offsetX, int offsetY) { TemplateResult tmplRet = MatchTemplate(ScreenNo, targetPath, threshold); if (tmplRet.MatchList.Count == 0) { return false; } var screen = Screen.AllScreens[ScreenNo]; Move(screen.Bounds.X + tmplRet.MatchList[0].X, screen.Bounds.Y + tmplRet.MatchList[0].Y); Click(); return true; } static public bool ClickImg(int ScreenNo, string targetPath, double threshold) { TemplateResult tmplRet = MatchTemplate(ScreenNo, targetPath, threshold); if (tmplRet.MatchList.Count == 0) { return false; } var screen = Screen.AllScreens[ScreenNo]; Move(screen.Bounds.X + tmplRet.MatchList[0].X + tmplRet.TargetWidth/ 2, screen.Bounds.Y + tmplRet.MatchList[0].Y + tmplRet.TargetHeight / 2); Click(); return true; } } class Program { static void Main(string[] args) { Console.ReadLine(); var targetPath = @"target.bmp"; GuiAuto.ClickImg(0, targetPath, 0.75); } } }このサンプルはスクリーン上に存在するtarget.bmpの画像を検索してクリックするものとなっています。
やっている内容としてはOpenCvのチュートリアルのTemplate Matchingと似たようなことです。
MatchTemplateは複数の類似画像の位置を取得できるようにFloodFillを実施してループしていますが、常に最も一致した画像だけを取得するならループは不要です。あとは、取得した位置をもとにマウスを移動してクリックしています。
なお、マルチディスプレイを考慮しているので、ClickImgのScreenNoを変更することで別のスクリーンを検索することが可能です。スクリーン上の画像の取得は.NETのよくあるキャプチャ処理で、取得したBitmapオブジェクトはOpenCvSharp.Extensions.BitmapConverter.ToMatで行っています。
OpenCvSharpは.NET2.0でも動作するのですが、どうも.NET2.0ではOpenCvSharp.Extensions.dllを提供していないようです。
自前でBitmapConvert.csと同様な処理を実装すればできるかもしれませんが、.NET3.5までは簡単にできましたが、.NET2.0ではうまくいきませんでした。PowerShell 5.1の例
Windows10 Home + PowerShell5.1でもC#と同様のことが行えます。
OpenCvSharpExtern.dllは使用するPowerShellがx86の場合はx86,x64の場合はx64を使用してください。
次に以下のようなスクリプトを記述して実行します。
$source = @" using OpenCvSharp; using System; using System.Collections.Generic; using System.Drawing; using System.Runtime.InteropServices; using System.Windows.Forms; public class GuiAuto { // https://culage.hatenablog.com/entry/20130611/1370876400 [DllImport("user32.dll")] extern static uint SendInput(uint nInputs, INPUT[] pInputs, int cbSize); [StructLayout(LayoutKind.Sequential)] struct INPUT { public int type; public MOUSEINPUT mi; } [StructLayout(LayoutKind.Sequential)] struct MOUSEINPUT { public int dx; public int dy; public int mouseData; public int dwFlags; public int time; public IntPtr dwExtraInfo; } const int MOUSEEVENTF_LEFTDOWN = 0x0002; const int MOUSEEVENTF_LEFTUP = 0x0004; static public void Click() { //struct 配列の宣言 INPUT[] input = new INPUT[2]; //左ボタン Down input[0].mi.dwFlags = MOUSEEVENTF_LEFTDOWN; //左ボタン Up input[1].mi.dwFlags = MOUSEEVENTF_LEFTUP; //イベントの一括生成 SendInput(2, input, Marshal.SizeOf(input[0])); } static public void Move(int x, int y) { var pt = new System.Drawing.Point(x, y); System.Windows.Forms.Cursor.Position = pt; } public class TemplateResult { public int TargetWidth { set; get; } public int TargetHeight { set; get; } public List<OpenCvSharp.Point> MatchList { set; get; } public TemplateResult() { this.MatchList = new List<OpenCvSharp.Point>(); } } static public TemplateResult MatchTemplate(int ScreenNo, string targetPath, double threshold) { TemplateResult result = new TemplateResult(); var screen = Screen.AllScreens[ScreenNo]; Bitmap bitmap = new Bitmap(screen.Bounds.Width, screen.Bounds.Height); Graphics graphics = Graphics.FromImage(bitmap as Image); graphics.CopyFromScreen(screen.Bounds.X, screen.Bounds.Y, 0, 0, bitmap.Size); using (var targetImg = Cv2.ImRead(targetPath)) using (var img = OpenCvSharp.Extensions.BitmapConverter.ToMat(bitmap)) using (var img3ch = img.CvtColor(ColorConversionCodes.BGRA2BGR)) { result.TargetWidth = targetImg.Width; result.TargetHeight = targetImg.Height; var tmplRet = img3ch.MatchTemplate(targetImg, TemplateMatchModes.CCoeffNormed); double minVal, maxVal; OpenCvSharp.Point minLoc, maxLoc; tmplRet.MinMaxLoc(out minVal, out maxVal, out minLoc, out maxLoc); Mat thresholdRet = tmplRet.Threshold(threshold, 1.0, ThresholdTypes.Tozero); while (true) { thresholdRet.MinMaxLoc(out minVal, out maxVal, out minLoc, out maxLoc); if (maxVal < threshold) { break; } result.MatchList.Add(maxLoc); thresholdRet.FloodFill(maxLoc, 0); } } return result; } static public bool ClickImg(int ScreenNo, string targetPath, double threshold, int offsetX, int offsetY) { TemplateResult tmplRet = MatchTemplate(ScreenNo, targetPath, threshold); if (tmplRet.MatchList.Count == 0) { return false; } var screen = Screen.AllScreens[ScreenNo]; Move(screen.Bounds.X + tmplRet.MatchList[0].X, screen.Bounds.Y + tmplRet.MatchList[0].Y); Click(); return true; } static public bool ClickImg(int ScreenNo, string targetPath, double threshold) { TemplateResult tmplRet = MatchTemplate(ScreenNo, targetPath, threshold); if (tmplRet.MatchList.Count == 0) { return false; } var screen = Screen.AllScreens[ScreenNo]; Move(screen.Bounds.X + tmplRet.MatchList[0].X + tmplRet.TargetWidth/ 2, screen.Bounds.Y + tmplRet.MatchList[0].Y + tmplRet.TargetHeight / 2); Click(); return true; } } "@ $dllPath = Split-Path $MyInvocation.MyCommand.Path Set-Item Env:Path "$Env:Path;$dllPath" Write-Host $currentDir $assemblies = @( "$dllPath\OpenCVSharp.dll", "$dllPath\OpenCvSharp.Extensions.dll", "System.Runtime", "System.Windows.Forms", "System.Drawing" ) Add-Type -TypeDefinition $source -ReferencedAssemblies $assemblies Add-Type -Path "$dllPath\OpenCVSharp.dll" Add-Type -Path "$dllPath\OpenCVSharp.Extensions.dll"実行結果
初期状態のWindows7のPowerShellでできないか?
難しいです。
理由として初期状態のWindows7では.NET3.5とPowerShell2.0が入っていますが、このPowerShell2.0はどんな新しい.NET Frameworkが入っていても.NET2.0を使用してしまいます。PowerShellでdllを読み込む際の注意点
https://qiita.com/icoxfog417/items/e0d29bed109071888f19このため、BitmapConvert.csと同様の処理が、うまく実装できませんでした。
やるなら、.NET Framework3.5でコマンドラインツールを作成して、PowerShellから呼び出す用な形になると思います(当然、起動時にオーバーヘッドがかかります)
まとめ
画像認識とかいうと難しく考えがちですが、OpenCvを利用すれば、わりと簡単に画像を利用した自動操作を自前でつくれます。
ただし、あまり古すぎる環境だと辛いです。
- 投稿日:2019-10-04T20:02:45+09:00
【Unity】Animation単体の時に、アニメーションを最初から再生する方法
はじめに
Animatorを使わずに、Animationのみで実装する系の記事って意外と(?)無い気がするんですよね…。
というか、Unityのアニメーション関連は検索するのが難しい…。
で、「Animation 最初から」とかで検索かけてもあまりヒットしなくて毎回苦労しているのでメモ。開発環境
Unity2019.1.8f1
巻き戻せば良いんや
再生する前に、最初の状態に戻しましょう。
「Animation 最初から」でググるからいけなかった。「巻き戻す」でググれば良かったんですね。PlayAnime.csAnimation anime; anime = GameObject.Find("Animationがアタッチされているオブジェクト").GetComponent<Animation>(); anime.Rewind("アニメーション名"); //0フレーム目に巻き戻す anime.Play("アニメーション名");Animation-Rewind - Unity スクリプトリファレンス
最後に
他にもこんな方法あるよーというのがございましたらぜひご教示ください!
- 投稿日:2019-10-04T17:11:31+09:00
Azure Functions の設定を Microsoft.Extensions.Configuration.Binder を使ってバインドする
Azure Functions の設定を Microsoft.Extensions.Configuration.Binder を使ってバインドする
さくっとできるかと思ったら大変だったので大変だったのでメモ.
Azure Portal から入れた
アプリケーション設定
や接続文字列
は環境変数を通して関数に渡される. Azure Functions v2 では以下のような感じになる.■ Key1=Value1のアプリケーション設定 (2行出る)
APPSETTING_Key1=Vaue1 Key1=Vaue1■ Key1=Value1の接続文字列 (種類=Custom)
CUSTOMCONNSTR_Key1=Value1EnvironmentVariablesConfigurationProvider が接続文字列についてはこれを
ConnectionStrings:Key1=Value1
に書き換えてくれるが、アプリケーション設定については放置である. しょうがないので自分で書き換えてくれるものを作った.AppSettingsProvider.csusing Microsoft.Extensions.Configuration; using System; using System.Collections; using System.Collections.Generic; using System.Linq; namespace FunctionApp1.Configs { public static class AppSettingsExtensions { public static IConfigurationBuilder AddAppSettings(this IConfigurationBuilder configurationBuilder) { configurationBuilder.Add(new AppSettingsConfigurationSource()); return configurationBuilder; } } public class AppSettingsConfigurationSource : IConfigurationSource { public IConfigurationProvider Build(IConfigurationBuilder builder) { return new AppSettingsProvider(); } } public class AppSettingsProvider : ConfigurationProvider { static readonly string Prefix = "APPSETTING_"; public override void Load() { var data = new Dictionary<string, string>(StringComparer.OrdinalIgnoreCase); foreach (var e in Environment.GetEnvironmentVariables().Cast<DictionaryEntry>()) { var key = (string)e.Key; if (!key.StartsWith(Prefix)) continue; data[$"AppSettings:{key.Substring(Prefix.Length)}"] = (string)e.Value; } Data = data; } } }後は設定を入れる POCO クラスを作ってバインドするだけである.
AppConfigs.csnamespace FunctionApp1.Configs { public class AppConfigs { public ConnectionStrings ConnectionStrings { get; set; } public AppSettings AppSettings { get; set; } } }AppSettings.csnamespace FunctionApp1.Configs { public class AppSettings { public string Key1 { get; set; } public string Key2 { get; set; } } }ConnectionStrings.csnamespace FunctionApp1.Configs { public class ConnectionStrings { public string Database { get; set; } public string AzureStorage { get; set; } } }Function1.csusing FunctionApp1.Configs; using Microsoft.AspNetCore.Http; using Microsoft.AspNetCore.Mvc; using Microsoft.Azure.WebJobs; using Microsoft.Azure.WebJobs.Extensions.Http; using Microsoft.Azure.WebJobs.Host; using Microsoft.Extensions.Configuration; using System.Text; namespace FunctionApp1 { public static class Function1 { [FunctionName("Function1")] public static IActionResult Run([HttpTrigger(AuthorizationLevel.Anonymous, "get", "post", Route = null)]HttpRequest req, TraceWriter log) { var configs = new ConfigurationBuilder() .AddEnvironmentVariables() .AddAppSettings() .Build() .Get<AppConfigs>(); var sb = new StringBuilder() .AppendLine(configs.AppSettings.Key1) .AppendLine(configs.AppSettings.Key2) .AppendLine(configs.ConnectionStrings.Database) .AppendLine(configs.ConnectionStrings.AzureStorage); return new ContentResult() { Content = sb.ToString(), ContentType = "text/plain", StatusCode = StatusCodes.Status200OK }; } } }
アプリケーション設定
にKey1=Value1, Key2=Value2
、接続文字列
にDatabase=Database ConnectionString!!, Azure Storage=AzureStorage ConnectionString!!
を入れてアクセスしたら以下の出力が得られた. 成功!Value1 Value2 Database ConnectionString!! AzureStorage ConnectionString!!
- 投稿日:2019-10-04T14:40:13+09:00
やさC#(乱数)
C#では同じタイミングで2つ乱数が宣言された場合は同じ乱数しかでない。
要するに一秒違いで出た処理については別々の数値がでるといった・・・何でそうしてるの?って仕組みが存在する
ここがjavaとは違うので要注意。
後程編集予定
- 投稿日:2019-10-04T14:33:31+09:00
やさC#(インデクサ)
過去に教えて頂いたインデクサをアウトプット
補足1
Javaにはない
Program.cs
using System; using System.Collections.Generic; namespace IndexerLesson { public class Colors { private string[] data = { "赤", "青", "黄" }; //アクセス 戻り値 this[型 引数] public string this[int index]{ set{ this.data[index] = value; } get{ return data[index]; } } } }Colors.cs
using System; using System.Collections.Generic; namespace IndexerLesson { public class Colors { private string[] data = { "赤", "青", "黄" }; public string this[int index] { set { this.data[index] = value; } get { return data[index]; } } } }オーバーロード可能
JMonth.cs
using System; using System.Collections.Generic; namespace IndexerLesson { public class JMonth { private string[] months = { "睦月", "如月", "弥生", "卯月", "皐月", "水無月", "文月", "葉月", "長月", "神無月", "霜月", "師走" }; public string this[int index] { get { return months[index - 1]; } } public int this[string name] { get { return Array.IndexOf(months, name) + 1; } } } }Profram.cs
using System; using System.Collections.Generic; namespace IndexerLesson { class Class1 { static void Main(string[] args) { JMonth jMonth = new JMonth(); Console.WriteLine(jMonth[6]); Console.WriteLine(jMonth["神無月"]); } } }って認識です。
- 投稿日:2019-10-04T12:39:23+09:00
【Unity(C#)】VIVE用にデバッグ時のリセット実装
デバッグでPCまで戻るのめんどくさい
デバッグはしらみつぶしに行いたいのでキー入力でリセットの実装にしてると
何度もPCとプレイ位置を往復するのでめんどうです。かなり短くてしょーもない記事ですが、今後毎回使いそうなのでメモします。
コード
ベースはまんま前の記事1ですが、、、
#if UNITY_EDITOR using UnityEngine; using UnityEngine.SceneManagement; using Valve.VR; public class DebugReload : MonoBehaviour { [SerializeField] SteamVR_Input_Sources hand; [SerializeField] SteamVR_Action_Boolean grabAction; [SerializeField] float inputSeconds; bool preventContinuityPushButton; float timer; void Update() { if (grabAction.GetStateDown(hand)) { preventContinuityPushButton = true; } if (grabAction.GetState(hand) == false) { timer = 0; return; } else if (grabAction.GetState(hand) && preventContinuityPushButton) { timer += Time.deltaTime; } if (timer > inputSeconds) { Scene loadScene = SceneManager.GetActiveScene(); SceneManager.LoadScene(loadScene.name); } } } #endifSteamVR 2.0からVIVEの入力回りは、最初に設定したアクションから取得する形になってます。
簡単に言えば、それぞれの入力にポーズ名が決まっているだけのことです。今回はGripButtonの取得をしたいので、GrabGripを設定すればOKです。
Platform Dependent Compilation(プラットフォーム依存コンパイル)2
下記のようにEditor上のみでコンパイルする設定にしておけば、
実行ファイル形式で書きだした際に、変な機能が残ったままにならないので便利です。#if UNITY_EDITOR #endif
- 投稿日:2019-10-04T07:21:46+09:00
Visual Studio for Macで最新版のXamarin FormsのMvvmCrossを使う方法
概要
Visual Studio for MacでXamarin FormsのMvvmCrossにおいて最新版(2019.10現在、6.4.1)を使用して開発をしたい場合に色々と苦労したので解決した際のメモ。
問題点
MvvmCross公式ドキュメントの導入で紹介されているVS for Mac用のテンプレートが古く、バージョン5.x用のテンプレートとなっている。
5.xと6.xでは破壊的な変更が数多くあり、単純にIDEの設定でランタイムのバージョンアップをするだけでは対応は不可。公式ドキュメントの導入
https://www.mvvmcross.com/documentation/getting-started/getting-started5.xと6.xへの変更方法(変更点多数)
https://www.mvvmcross.com/documentation/upgrading/upgrade-to-mvvmcross-60?scroll=2083唯一のVS For Macのテンプレート(MvvmCross Template Pack)
http://addins.monodevelop.com/Project/Index/227解決策
最終的にWindowsのVisual Studioを使用した。
- 6.xに対応したテンプレートでプロジェクトを作成
- GitHubなりUSBなりでMac側に持ってきて、VS for Macでそのまま開く事で使うことができた。
今回はMvxScaffoldingといったテンプレート作成のアドインを使用して作成した。
デフォルトだと最新の6.4になっていないので、あとはNugetでバージョンアップを行うことで最新バージョンのものが使用可能。
- 投稿日:2019-10-04T07:21:46+09:00
Visual Studio for MacでXamarin Formの最新版MvvmCrossを使う方法
概要
Visual Studio for Mac(2019)でXamarin FormsのMvvmCrossにおいて最新版(2019.10現在、6.4.1)を使用して開発をしたい場合に色々と苦労したので解決した際のメモ。
問題点
MvvmCross公式ドキュメントの導入で紹介されているVS for Mac用のテンプレートが古く、バージョン5.x用のテンプレートとなっている。
5.xと6.xでは破壊的な変更が数多くあり、単純にIDEの設定でランタイムのバージョンアップをするだけでは対応は不可。公式ドキュメントの導入
https://www.mvvmcross.com/documentation/getting-started/getting-started5.xと6.xへの変更方法(変更点多数)
https://www.mvvmcross.com/documentation/upgrading/upgrade-to-mvvmcross-60?scroll=2083唯一のVS For Macのテンプレート(MvvmCross Template Pack)
http://addins.monodevelop.com/Project/Index/227解決策
最終的にWindowsのVisual Studioを使用した。
- 6.xに対応したテンプレートでプロジェクトを作成。
- GitHubなりUSBなりでMac側に持ってきて、slnファイルをVS for Macでそのまま開く事で使うことができた。
今回はMvxScaffoldingといったテンプレート作成のアドインを使用して作成した。
デフォルトだと最新の6.4になっていないので、あとはNugetでバージョンアップを行うことで最新バージョンのものが使用可能。
- 投稿日:2019-10-04T00:48:30+09:00
C言語とC#のおまじない卒業に向けて
おまじないを卒業しよう。
ということで、ごった煮な内容でお届けしたいと思います。
詳しい説明はリンク先頼みな内容になる予定。。。
Cを混ぜたのは早くも失敗した感がしている。気が向いたら追記していきます。
なぜ「おまじない」のままではダメなのか
プログラミングの業界で「おまじない」というと、「よく分からないけど」とりあえず書いてみたといったニュアンスだと思います。
1文字間違えただけで致命的なバグになったりする世界で、「よく分からないけどみんな書いてるから」で書くのは恐ろしいことではないでしょうか?
基本を理解していないと、どこかでハマることになります。という事で、分からないままにするのはよろしくないと思うわけです。
Cにおける
#include
実際はなにをやっているのか?
⇒ 参照先ファイル(通常はヘッダファイル)の内容をただ単純にコピーして展開するもの。#define
もただ置き換えるだけなので、そういうものだと思っておけばよいかと。
・参考サイト: https://monozukuri-c.com/langc-include/C#における
using System;
とか名前空間を省略して書けるようにするもの。
例えばusing System;
って書いておけば、System.IntPtr
をIntPtr
と書いてもコンパイラが探して補完くれる。
・参考サイト: https://ufcpp.net/study/csharp/sp_namespace.html
・C#本家(英語): https://docs.microsoft.com/ja-jp/dotnet/csharp/language-reference/keywords/using-directiveなお、上記は「usingディレクティブ」といい、
using(型名 変数名=xxx){処理}
は「usingステートメント」といい、別モノです。C#における
[STAThread]
属性スレッドの管理がイケてないCOM機能や、間接的にそのCOM使っている.Netのクラスを使うときは
[STAThread]
つけてね
という感じ。
つけ忘れてハマるよりは、つけておくほうが無難。
(Single-Thread ApartmentとMulti-Thread Apartmentの概念が理解できておらず、おまじないレベルから脱却できてない・・・)
参考サイト: https://dobon.net/vb/dotnet/form/stathread.htmlメイン関数
C,C#とも、コンパイラに、最初に開始する処理がどこにあるかを判断させるために、あらかじめ決められた関数名(メソッド名)を使っている。dll作るときは別の手段を取る。