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

C# マイク入力・WAVEファイル保存 Win32API

PCのマイクから音声を取得し、WAVE形式のファイルとして保存する。 環境 Windows 10 visual studio 2019 C# Win32API(WinMM) WPF(32bit)※WinMMが32bit対応のため 実装 ソースコード 録音時間(秒数)をしてして、ボタン押下で録音開始。 録音完了後、ファイルダイアログを表示し録音データをWAVE形式のファイルとして保存する。 Win32API(WinMM) waveInOpen 関数 入力のデバイスをオープンする。 戻り値が「MMSYSERR_NOERROR(0)」の場合、正常終了。 NativeMethods.cs [DllImport("winmm.dll", CharSet = CharSet.Unicode, SetLastError = true, CallingConvention = CallingConvention.StdCall)] public static extern int waveInOpen(ref IntPtr hwi, int uDeviceID, ref WaveFormatEx wfx, IntPtr dwCallback, IntPtr dwCallbackInstance, int fdwOpen); 引数名 概要 hwi オープンした入力デバイスのハンドルが格納される。 uDeviceID オープンする入力デバイスの識別子を指定する。「WAVE_MAPPER(-1)」を指定するとデフォルトの入力デバイスを指定できる。 wfx 「WaveFormatEx構造体」 dwCallback 入力デバイスからのコールバックを受け取るハンドルを設定する。 dwCallbackInstance コールバック時に受け取ることができるインスタンスを設定する。必要ない場合は0を設定する。 fdwOpen コールバックを受け取るハンドルの種類を指定する。 waveInOpen関数の引数「fdwOpen」に設定する値。 NativeMethods.cs public const int CALLBACK_WINDOW = 0x10000; public const int CALLBACK_FUNCTION = 0x30000; 定数名 概要 CALLBACK_WINDOW Windowハンドルで受け取る場合 CALLBACK_FUNCTION 通常の関数コールバック 通常の関数コールバックで受ける場合は、下記のように「dwCallback」をデリゲートにしてもいい。 NativeMethods.cs public delegate void DelegateWaveInProc(IntPtr hwi, uint uMsg, IntPtr dwInstance, IntPtr dwParam1, IntPtr dwParam2); [DllImport("winmm.dll", CharSet = CharSet.Unicode, SetLastError = true, CallingConvention = CallingConvention.StdCall)] public static extern int waveInOpen(ref IntPtr hwi, int uDeviceID, ref WaveFormatEx _wfx, DelegateWaveInProc dwCallback, IntPtr dwCallbackInstance, int fdwOpen); 関数コールバックの引数の説明 引数名 概要 hwi オープンした入力デバイスのハンドル uMsg コールバックメッセージ dwInstance waveInOpen関数で指定したインスタンス dwParam1 録音された「WaveHdr構造体」 dwParam2 今回使用されていない コールバックメッセージ NativeMethods.cs public const int WIM_OPEN = 0x3BE; public const int WIM_CLOSE = 0x3BF; public const int WIM_DATA = 0x3C0; 定数名 概要 WIM_OPEN 入力デバイスがオープンした場合 WIM_CLOSE 入力デバイスがクローズした場合 WIM_DATA バッファに貯まった録音データがいっぱいになった場合 WaveFormatEx 構造体 録音時のデータ形式を設定する。 NativeMethods.cs [StructLayout(LayoutKind.Sequential, CharSet = CharSet.Unicode)] public struct WaveFormatEx { public Int16 wFormatTag; public Int16 nChannels; public int nSamplesPerSec; public int nAvgBytesPerSec; public Int16 nBlockAlign; public Int16 wBitsPerSample; public Int16 cbSize; } 変数名 概要 wFormatTag データ形式 nChannels チャンネル数 nSamplesPerSec サンプリング周波数 nAvgBytesPerSec 1秒間あたりのバイト数 nBlockAlign 1サンプルあたりのバイト数 wBitsPerSample ビット数 cbSize 拡張データのバイト数 waveInClose 関数 入力デバイスをクローズする。戻り値が「MMSYSERR_NOERROR(0)」の場合、正常終了。 NativeMethods.cs [DllImport("winmm.dll", CharSet = CharSet.Unicode, SetLastError = true)] public static extern int waveInClose(IntPtr hwi); 引数名 概要 hwi 入力デバイスのハンドル waveInPrepareHeader 関数 録音データを格納する「WaveHdr構造体」を入力デバイスで使用できるよう準備する。戻り値が「MMSYSERR_NOERROR(0)」の場合、正常終了。 NativeMethods.cs [DllImport("winmm.dll", CharSet = CharSet.Unicode, SetLastError = true)] public static extern int waveInPrepareHeader(IntPtr hwi, ref WaveHdr wh, int cbwh); 引数名 概要 hwi 入力デバイスのハンドル wh 「WaveHdr構造体」 cbwh 「WaveHdr構造体」のサイズ waveInUnprepareHeader 関数 waveInPrepareHeader関数で準備した「WaveHdr構造体」を開放する。録音中、C#では「WaveHdr構造体」がガーベージコレクタによって自動で開放されないようする必要がある。戻り値が「MMSYSERR_NOERROR(0)」の場合、正常終了。 NativeMethods.cs [DllImport("winmm.dll", CharSet = CharSet.Unicode, SetLastError = true)] public static extern int waveInUnprepareHeader(IntPtr hwi, ref WaveHdr wh, int cbwh); 引数名 概要 hwi 入力デバイスのハンドル wh 「WaveHdr構造体」 cbwh 「WaveHdr構造体」のサイズ waveInAddBuffer 関数 録音データを書き込むバッファ「WaveHdr構造体」を入力デバイスに追加する。戻り値が「MMSYSERR_NOERROR(0)」の場合、正常終了。 NativeMethods.cs [DllImport("winmm.dll", CharSet = CharSet.Unicode, SetLastError = true)] public static extern int waveInAddBuffer(IntPtr hwi, ref WaveHdr wh, int cbwh); 引数名 概要 hwi 入力デバイスのハンドル wh 「WaveHdr構造体」 cbwh 「WaveHdr構造体」のサイズ WaveHdr 構造体 NativeMethods.cs [StructLayout(LayoutKind.Sequential, CharSet = CharSet.Unicode)] public struct WaveHdr { public IntPtr lpData; public int dwBufferLength; public int dwBytesRecorded; public IntPtr dwUser; public int dwFlags; public int dwLoops; public IntPtr lpNext; public int reserved; } 変数名 概要 lpData 録音データ格納バッファ dwBufferLength バッファサイズ dwBytesRecorded データのバイト数 dwUser ユーザーが使用できる領域 dwFlags バッファの状態を示すフラグ dwLoops データ出力時の入力ループ回数を示す。 lpNext 予約語 reserved 予約語 waveInStart 関数 入力デバイスからの録音をスタートする。 戻り値が「MMSYSERR_NOERROR(0)」の場合、正常終了。 NativeMethods.cs [DllImport("winmm.dll", CharSet = CharSet.Unicode, SetLastError = true)] public static extern int waveInStart(IntPtr hwi); 引数名 概要 hwi 入力デバイスのハンドル waveInStop 関数 入力デバイスからの録音をストップする。 戻り値が「MMSYSERR_NOERROR(0)」の場合、正常終了。 NativeMethods.cs [DllImport("winmm.dll", CharSet = CharSet.Unicode, SetLastError = true)] public static extern int waveInStop(IntPtr hwi); 引数名 概要 hwi 入力デバイスのハンドル マイク入力開始 入力デバイスのオープン MicSoundRecorder.cs // コールバック関数のデリゲートを作成 _WaveProc = new NativeMethods.DelegateWaveInProc(WaveInProc); // データ形式を設定する。 _WaveFormat = new NativeMethods.WaveFormatEx(); _WaveFormat.wFormatTag = NativeMethods.WAVE_FORMAT_PCM; // リニア PCM _WaveFormat.cbSize = 0; _WaveFormat.nChannels = 1; _WaveFormat.nSamplesPerSec = 11025; _WaveFormat.wBitsPerSample = 8; _WaveFormat.nBlockAlign = (short)(_WaveFormat.wBitsPerSample / 8 * _WaveFormat.nChannels); _WaveFormat.nAvgBytesPerSec = _WaveFormat.nSamplesPerSec * _WaveFormat.nBlockAlign; // 入力デバイスオープン var result = NativeMethods.waveInOpen(ref _Hwi, NativeMethods.WAVE_MAPPER, ref _WaveFormat, _WaveProc, IntPtr.Zero, NativeMethods.CALLBACK_FUNCTION); 「_WaveProc 」「_WaveFormat」「_Hwi」はガーベージコレクタによって破棄されないようにする。ここではクラス変数を想定。 録音開始 録音は非同期で動作する。 MicSoundRecorder.cs // 録音データを書き込むバッファを作成 var dataSize = _WaveFormat.nAvgBytesPerSec * recordSecond; _WaveHdr = new NativeMethods.WaveHdr(); _WaveHdr.lpData = Marshal.AllocHGlobal(dataSize); // メモリを確保 _WaveHdr.dwBufferLength = dataSize; // バッファを準備・追加 var cdwh = Marshal.SizeOf<NativeMethods.WaveHdr>(); NativeMethods.waveInPrepareHeader(_Hwi, ref _WaveHdr, cdwh); NativeMethods.waveInAddBuffer(_Hwi, ref _WaveHdr, cdwh); // 録音スタート NativeMethods.waveInStart(_Hwi); 入力データ待受 非同期でメッセージ受信 MicSoundRecorder.cs public void WaveInProc(IntPtr hwi, uint uMsg, IntPtr dwInstance, IntPtr dwParam1, IntPtr dwParam2) { switch (uMsg) { case NativeMethods.WIM_OPEN: // オープン WaveOpen?.Invoke(this, EventArgs.Empty); break; case NativeMethods.WIM_CLOSE: // クローズ WaveClose?.Invoke(this, EventArgs.Empty); break; case NativeMethods.WIM_DATA: // var wh = Marshal.PtrToStructure<NativeMethods.WaveHdr>(dwParam1); // WAVEデータ OnWaveData(); break; default: break; } } WAVEファイル作成 録音データにWAVEヘッダを添付して、外部でも再生できるようにする。 WAVEヘッダ WAVEヘッダサイズは44バイト 最高4GBまでのファイルを扱うことができる。 項目名 設定値 サイズ RIFF識別子 “RIFF”固定 4バイト チャンクサイズ 全体のファイルサイズ(ヘッダ+録音データサイズ)-8バイト 4バイト フォーマット “WAVE”固定 4バイト fmt識別子 “fmt “固定。 4バイト fmtチャンクのバイト数 16+拡張バイト(リニア PCMの場合0) 4バイト データ形式 1(リニア PCM) 2バイト チャンネル数 1(モノラル)、2(ステレオ) 2バイト サンプリング周波数 例 11025 4バイト 1 秒あたりバイト数の平均 例 11025 4バイト 1サンプルあたりのバイト数 例 8 2バイト ビット数 例 8 2バイト サブチャンク識別子 “data”固定 4バイト サブチャンクサイズ 録音データサイズ 4バイト WAVEデータ作成 MicSoundRecorder.cs private void OnWaveData() { // WAVEデータをコピーするバイト配列を作成 var headerSize = 44; var dataSize = _WaveHdr.dwBufferLength + headerSize; var waveData = new byte[dataSize]; // WAVEヘッダを設定 Array.Copy(Encoding.ASCII.GetBytes("RIFF"), 0, waveData, 0, 4); Array.Copy(BitConverter.GetBytes((uint)(dataSize - 8)), 0, waveData, 4, 4); Array.Copy(Encoding.ASCII.GetBytes("WAVE"), 0, waveData, 8, 4); Array.Copy(Encoding.ASCII.GetBytes("fmt "), 0, waveData, 12, 4); Array.Copy(BitConverter.GetBytes((uint)16), 0, waveData, 16, 4); Array.Copy(BitConverter.GetBytes((ushort)(_WaveFormat.wFormatTag)), 0, waveData, 20, 2); Array.Copy(BitConverter.GetBytes((ushort)(_WaveFormat.nChannels)), 0, waveData, 22, 2); Array.Copy(BitConverter.GetBytes((uint)(_WaveFormat.nSamplesPerSec)), 0, waveData, 24, 4); Array.Copy(BitConverter.GetBytes((uint)(_WaveFormat.nAvgBytesPerSec)), 0, waveData, 28, 4); Array.Copy(BitConverter.GetBytes((ushort)(_WaveFormat.nBlockAlign)), 0, waveData, 32, 2); Array.Copy(BitConverter.GetBytes((ushort)(_WaveFormat.wBitsPerSample)), 0, waveData, 34, 2); Array.Copy(Encoding.ASCII.GetBytes("data"), 0, waveData, 36, 4); Array.Copy(BitConverter.GetBytes((uint)(_WaveHdr.dwBufferLength)), 0, waveData, 40, 4); Marshal.Copy(_WaveHdr.lpData, waveData, headerSize, _WaveHdr.dwBufferLength); // バッファを開放 NativeMethods.waveInUnprepareHeader(_Hwi, ref _WaveHdr, Marshal.SizeOf<NativeMethods.WaveHdr>()); // メモリ開放 Marshal.FreeHGlobal(_WaveHdr.lpData); WaveData?.Invoke(this, waveData); } 参考 https://qiita.com/kob58im/items/aa6c7a4dc80946dbe3a7 https://github.com/ttsuki/ttsuki/blob/master/WinMM/WaveIO.cs http://wisdom.sakura.ne.jp/system/winapi/media/mm7.html http://eternalwindows.jp/winmm/wave/wave04.html https://www.katto.comm.waseda.ac.jp/~katto/Class/01/GazoTokuron/code/audiocapture.html https://kana-soft.com/tech/sample_0010_2.htm#WAVEFORMATEX マイクロソフト https://docs.microsoft.com/en-us/previous-versions/dd743849(v=vs.85) https://docs.microsoft.com/ja-jp/windows/win32/api/mmeapi/ns-mmeapi-wavehdr https://docs.microsoft.com/ja-jp/windows/win32/api/mmeapi/ns-mmeapi-waveformatex WAVEヘッダに関して https://tomosoft.jp/design/?p=9107 https://www.youfit.co.jp/archives/1418
  • このエントリーをはてなブックマークに追加
  • Qiitaで続きを読む

C#のNLogをそのまま使って、C++/CLIを介してNative C++でログを取る

概要  C++でバックエンド、C#やHTML5+JavaScriptでフロントエンド、という案件は、よくある世の中。  ところで、C#でログファイルを定義しているのに、バックエンド側であるC++に渡せなくてちょっと悔しい思いをしている人はいないでしょうか。(基本的に古くてレガシーな技術なので、今後も使えるという保証はありませんが)C++/CLIを介して、C#で定義されたNLogをC++で使うことが出来ますので、今回はそれを紹介。  というか、C++/CLIを最も有効に使えている方法かもしれないと個人的に思う方法。普通に考えて、こんな複雑なことが出来るとは・・・  私もやってみて、MicrosoftがSQL ServerなどでC++/CLIやILなどの技術をなかなか捨てられん理由がよう分かりました。 参考記事  こちらの記事を参考に  ここの内容にある「妥協点」の直接的な解決法だと思います。 制約条件 C++がダイナミックライブラリの場合は、クラスオブジェクトを渡せないので、使用できません。スタティックライブラリを使うとき専用の方法です。 全体観  ちょっとややこしすぎるだろ~という内容ですが、原理的にはis-a関係、has-a関係を組み合わせまくることで何とかなります。ただシングルトンを多用するので、ログ吐き用途などの相当汎用的な機能ではない限り、普通の機能として使うのはちょっと向いていないです。あと、C++/CLIを使う前提のラッパーを書ける人なんてそんなに多くいるのだろうか C++/CLIでは、C++側にNativeLoggingを継承したClrLoggingのオブジェクトを渡します。そうすると、C++側では、NativeLoggingのメソッドを実行することで、C++/CLIで定義されたメソッドが実行されます。(ポリモーフィズム(多態性)に基づいた考え方です。ピンとこなかった人はオブジェクト指向の勉強を少ししておきましょう。) Loggingクラスはシングルトンです。したがって、ClrLoggingからLoggingを直接実行することが出来てしまいます。ということは、C++側からのNativeLoggingの実行に対して、Loggingの実行メソッドを用意しておけば、マネージド側の関数を実行することが出来ます。 C++/CLIのクラスLoggingから、C#でUserNLogを継承します。UserNLogは、NLogの実体であるLogManagerを呼び出すことが出来ます。 ポイント この方法のミソは、ClrLoggingからシングルトンを経由してLoggingを呼び出すことが出来てしまう点です。本来このような使い方は、どちらかと言えば禁忌とされることが多いと思いますが、出来てしまうのが驚きです. この方法は、ある意味では、現時点でのC++からC#のコードを実行する特殊な手段の一つです。この方法を解説する記事はあんまりないと思います(需要もないけどね).一応、C++/CLIのデフォルトにはないはずのnugetライブラリとか、githubからクローンしてきたライブラリなんかも使えるのがおいしいかも そういえば・・・  .NET Framework時代では、C++/CLIからC#のライブラリを実行することは比較的容易で、それはC#のライブラリもC++/CLIのライブラリも割とパッケージの依存性が近かったのですが、近年のVS2017, VS2019ではC++/CLIからC#のパッケージをデフォルトの画面を使って設定する機能が削減されている気がします。 Referencesからパッケージを設定できるのですが、NLogのようにnuget経由で取ってきたパッケージを検知しないような動きになっていますね。その関係で、C++/CLIは、昨今発展を続けているOSSの恩恵を受けにくくなっています。将来的には、個人的にはラッパーとしての機能は便利なので、そこだけは残してほしいかなあ・・・。 正直、早くCppSharpやSWIGなどのクロスプラットフォーム技術に乗り換えたいという思いはある 実行結果 こんな感じで、下のコードでは定義していないDEBUGが表示されているのがいい。日付のフォーマットやログフォーマット、ログレベルが変え放題なのも素晴らしい。 バックエンドの処理ログって、大体低レベル水準に落としたいという思いが強い気がするので、C++用のログレベルを落として、C#用のログレベルを上げる、みたいな器用なことも出来そうですね。 ソースコード ややこしいのでざっくり紹介します NativeLogging.h class NativeLogging { private: static NativeLogging* _logging_global; public: NativeLogging() { } NativeLogging(const NativeLogging& obj) { } virtual ~NativeLogging() { } virtual void PrintInfo(std::wstring text) = 0; virtual void PrintError(std::wstring text) = 0; public: static void SetGlobalLoggingObject(NativeLogging* logging); static NativeLogging* LogObject(); /* おまけ:実際の使い方 */ template <typename T> static bool ExecuteAndReturn(T func, std::wstring error_message) { int Ret = func(); /* ラムダ式で出てきた結果を受けて、その結果の返り値が0以外の場合はエラーを出力 */ if (Ret != 0) { std::wstringstream logging; logging << error_message << L":" << std::to_wstring(Ret); /* こんな感じで使う */ SysLog::NativeLogging::LogObject()->PrintError(logging.str()); return false; } return true; } }; 基本的には説明通りですが、最後に少しおまけ(使用例)をつけています。ラムダ式funcを受けて、その結果でエラーが出る場合にエラーメッセージを出す、みたいな書き方をしています。 PrintInfo, PrintErrorのように、抽象化しておいて、子クラスであるClrLoggingに明確に渡す書き方をするのがポイントです。 Logging.h #include "NativeLogging.h" #include <msclr/marshal_cppstd.h> using namespace msclr::interop; public ref class Logging { private: static Logging^ _log_object; private: SysLog::NativeLogging* _log_ptr = nullptr; public: Logging() { _log_object = nullptr; } Logging(const Logging% obj) { } !Logging() { this->~Logging(); } virtual ~Logging() { } static void InitInstance(Logging^ logging) { _log_object = logging; } static Logging^ GetInstance() { return _log_object; } public: virtual System::Void PrintInfo(System::String^ text) { /* ここの内容をC#では、NLogの内容として設定する */ } virtual System::Void PrintError(System::String^ text) { /* ここの内容をC#では、NLogの内容として設定する */ } public: void SetLoggingPtr(SysLog::NativeLogging* log_ptr) { _log_ptr = log_ptr; } void PrintInfoNative(std::wstring text) { this->PrintInfo(marshal_as<System::String^>(text)); } void PrintErrorNative(std::wstring text) { this->PrintError(marshal_as<System::String^>(text)); } }; class ClrLogging : public SysLog::NativeLogging { public: ClrLogging() { } ClrLogging(const ClrLogging& obj) { } virtual ~ClrLogging() {} void PrintInfo(std::wstring text) override { Logging^ obj = Logging::GetInstance(); obj->PrintInfoNative(text); } void PrintError(std::wstring text) override { Logging^ obj = Logging::GetInstance(); obj->PrintErrorNative(text); } }; 先ほどのコメントの通り・・・です。PrintInfo, PrintErrorの実装に、PrintInfoNativeやPrintErrorNativeを実行して、これをシングルトンから呼び出しているのがポイントかな。 UserNLog.cs public class UserNLog : Logging { private static Logger logger = LogManager.GetCurrentClassLogger(); public static void Init() { InitInstance(new UserNLog()); } public override void PrintInfo(string str) { /* ここの内容をC#では、NLogの内容として設定する */ logger.Info(str); } public static void PrintDebug(string str) { /* ここの内容をC#では、NLogの内容として設定する */ logger.Debug(str); } public static void PrintWarning(string str) { logger.Warn(str); } public override void PrintError(string str) { logger.Error(str); } }
  • このエントリーをはてなブックマークに追加
  • Qiitaで続きを読む

動的とか静的とか、プロパティとかメソッドとか、そういうのをはっきりさせたいだけ (2/?)

smileBASICから始まり pythonを学校で習い luaで遊んで C#を知る この知るを使いこなすまで行けられればいいのだが。 おっと、失礼。 タイトルが動的とか静的とか、プロパティとかメソッドとか、そういうのをはっきりさせたいだけのくせに 前回触れたのはプロパティとは何か、そしてメソッドとは何か、だけだ。 一切本質に触れてすら、かすってすらいない。 あ、いや、かすってはいるが、擦り傷程度にすらならない。 今回はとにかく 前回のまとめと、動的変数とプロパティの違い、動的が出るならその対になる概念、静的について、 さらに動的と静的の違い、=>演算子をはっきりさせていこうと思う。 とにかく先に前回のまとめ 名前空間 バニラとModやMod同士の競合などの問題を防ぐため、 どのコンテンツのものなのかを表すものであり、 それ以上の役割は存在しない。 メンバー class内に書かれる、変数やメソッド、プロパティなどのこと。 つまり、classの要素を表す プロパティ <アクセス修飾子> <型> <名前> { get; set; }のこと メソッド <アクセス修飾子> <返す値の型> <名前>([<引数1の型> <引数1の名前>[,<引数2の型> <引数2の名前>[,... ]]]) { }のこと 関数のこと(多分) アクセス修飾子 public, private, internal, protectedのこと インスタンス(オブジェクト)の作成 <型> <変数名> = new <型>();のこと アセンブリ コンパイルされてできた、アプリケーション(.exeファイル)や、Dynamic Link Library(.dllファイル)のことを指す 少なくともコードのことではない 追記 (ピンと来ない人向け) おそらくなのだが、ピンと来ていない人もいるのではないのかと。 Mod製作をしたことがある人なら、アクセス修飾子と多少関わることに関しては絶対分かると思う。 .dllファイルのdllはDynamic Link Librariesと言い、 使い方の1つとして(?) 他のコードからusingを使って、 dllファイルの中身(コード)にアクセスして使用できるのだ。 exeファイルは基本アプリケーションだが、dllファイルと同じように、アクセスできるものもある。 そしてそのようなexeファイルやdllファイルをまとめてアセンブリと言うらしい。 生のコードの状態で参照(アクセス)するか、 コンパイルされた後のものを使うかってところかな こんなところであろう。 アクセス修飾子について まだ前回のが完全に終わっていなかったな。 これをはっきりさせてしまおう。 上の単語がきちんと理解できているのなら、次の文章も難なく読めるのではないのだろうか。 public: この型またはメンバーには、同じアセンブリ内の他のコードや、そのアセンブリを参照する別のアセンブリ内の任意のコードからアクセスできます。 private:この型またはメンバーには、同じ class 内または同じ struct 内のコードからのみアクセスできます。 protected:この型またはメンバーには、同じ class 内のコードか、その class から派生した class 内のコードからのみアクセスできます。 internal:この型またはメンバーには、同じアセンブリ内の任意のコードからアクセスできますが、別のアセンブリからはアクセスできません。 (C# 公式リファレンス より) 「型」や「struct」など不明瞭なものが多少あるものの、理解はできる。 ここに載せなかった2つも含めて、要約しよう。 public どこからでもアクセスして使えるよ! (classから別のclassで使える dllファイルから別のclassで使える 継承先で使える) private 同じclass内じゃないとアクセスできないよ! (classから別のclassで使えない dllファイルから別のclassで使えない 継承先で使えない?) protected 同じclass内じゃないとアクセスできないよ! ただし、継承先ではアクセスできるよ! (classから別のclassで使えない dllファイルから別のclassで使えない 継承先で使える) internal 同じアセンブリだったらいいよ! = コンパイルされた後は、外部からのアクセスを拒否するよ! (classから別のclassで使える dllファイルから別のclassで使えない 継承先で使える) protected internal 同じアセンブリだったらいいよ! そして、別アセンブリでも継承先ではアクセスできるよ! (classから別のclassで使える dllファイルから別のclassで使えない? 継承先(別アセンブリ含む)で使える) private protected これがいまいち分からない (classから別のclassで使えない? dllファイルから別のclassで使えない 継承先で使える?) あぁ、そうだ。 readonlyというのもあったな。 これはまた今度だな。 そしてもう1つ、 「アクセス」というのはどこまでを表すんだ? 特に継承先で使用できるのか、それともoverrideまでできるのかと言うのも気になった。 これは実際に試してみた方が速そうだね。 やってみよう! ...と思ってのだが... 継承元は必ずabstract classやvirtual voidなどでないといけないのか、 まだそこがはっきりしていないので abstractやvirtualの理解を深めてから 詳しい実験をしたいと思う。 ともあれ、private protectedを除けば理解した。 後半の2つだが、私には経験が浅く、まだ見たことのない深淵であった。 それにsealed(継承禁止)を正しく理解すれば、 その深淵を照らすことができるかもしれない... 動的とプロパティの違い と 動的と静的の違い さて、本題だ。 大分長くなってしまったのように思えるな... 私が一番理解したいもの、それは <アクセス修飾子> <型> <変数名>; または <アクセス修飾子> <型> <変数名> = <値>; と <アクセス修飾子> <型> <プロパティ名> { get; set; }または<アクセス修飾子> <型> <プロパティ名> => <値>; の違いである。 早速調べていこう。 C# 変数 プロパティ 違い 検索 ヒットしたのは公式リファレンスではなかった。 あの最初の意味の分からなさを実感したかったのだが。 ふむふむ... 1つ気づいたことがある。 変数と初回から言ってきたのだが、 どうやら変数のことをフィールドまたはメンバ変数というようだ。 メンバ変数というのならば、もしかしたら別に変数が存在するのかもしれない。 C#では(私のレベルでは)必ずクラス宣言から始まる。 PythonやLuaなどにはなかった概念である。 おそらく、変数と関数は、別に存在するのかもしれない。 私の知っているプログラム(プログラミング)が崩れ去った。 崩れ去ったものを片付ける前に とりあえず気になったのを見てみる。 そして瓦礫から必要であれば引っ張てくる。 関数は残念ながら拾い上げることはできなかった。 だがしかし! もう納得した! 一度これについて調べたという記憶の断片が、瓦礫を再構築していくような感じだった。 まとめよう。 フィールド(メンバ変数)とプロパティとメソッド プロパティに関してだが、 私はよく public bool isItem { get; set; } のような形を見ることが多かったが、 実際はこれは省略形であり、本来はこのようになっている。 private bool _isItem; public bool isItem { get { return _isItem; } set { _isItem = value; } } /* 私はいつもは public bool isItem { } の形で書くことが多い(のはVisual Studioが勝手にそうするから)のだが プロパティでこの形使うとなんか違和感あるんだよね。 */ さて、get及びsetの中はどうなっている? まるでメソッドを書くような感じで書くのだ。 さぁ! 答えはもう間近だ!! フィールド classのデータを担う、PythonやLuaで言うところの変数である。 値を格納しておくもの。 メソッド classの振る舞いを担う(ちょっと何言ってるのか分からない)、PythonやLuaで言うところの関数である。 フィールドを変換したり、表示したりするなどの、 ステートメント(;までの1文のことらしい)の塊。 値を処理するもの。 プロパティ フィールドとメソッドの2つを足して2で割ったようなもの。 主にフィールドへの代入やフィールドの取得の役割を担う。 正直プロパティなどなくても良さそうに思えるが、あるからには意味があり、 場面によってはプロパティを用いた方が最適だったり正確であったりするのだろう。 このプロパティに関しては使い方も学んだ方が絶対いいと思うのだが 今は一旦置いておく。 本来であれば、さらにコンストラクタも説明した方がいいのだろう。 だがしかし、知っていました。 ざっくり言えばpythonで言うところのdef __init__(self)のようなものだ。 先に全ての本題を済ませておこう。 動的と静的の違い 「かえりち」と打って、その最初の予測変換を見てみよう! 「返り値」と出たかな?おめでとう!君はプログラマだ! いたぞ 連れていけ #プログラマ狩り (架空のツイート) みたいな感じで もうこんなに静的って書いてたら 予測変換の1番最初に「静的」が出るようになったよ! 連れてかれそう(小並感) えぇと、、、 静的だな、実はクラスについて調べた時(公式リファレンス)に、 静的についても書いてあって、さらに詳細のリンクがあった。 読んでみよう。 静的クラスは基本的には非静的クラスと同じですが、静的クラスはインスタンス化できないという点が異なります。 つまり、new 演算子を使用して、そのクラス型の変数を作成することはできません。 インスタンス変数がないため、静的クラスのメンバーにアクセスするには、クラス名自体を使用します。 (C# 公式リファレンス より) え。 分かってしまうのだが。 そしてどうやら、動的のことを非静的と言うようだ。 理解...してしまった... いや!ちょっと待ってくれ! ここで書かれているのは静的クラスだ! メンバーは? static修飾子について書かれているページにとんだ。 static 修飾子は、静的メンバーの宣言に使用します。静的メンバーは、特定のオブジェクトではなく、型自体に属するメンバーです。 static 修飾子は static クラスの宣言に使用できます。 クラス、インターフェイス、構造体では、static 修飾子をフィールド、メソッド、プロパティ、演算子、イベント、コンストラクターに追加できます。 static 修飾子はインデクサーおよびファイナライザーと併用できません。 待ってました!意味が分からない単語!! あああああああ!最高!だけど!やめて! と歓喜という名の発狂をしつつ読んだのだが、 これ 現時点で そもそも「静的」がどのようなものなのか、一切説明がなされていない そもそも静的とは 正解はわりと近くにあった。 ページを戻して、少し下にスクロールしたところに。 静的クラスは、入力パラメーターに対してのみ処理を行い、内部のインスタンス フィールドを取得したり設定したりする必要のない一連のメソッドを格納する、便利なコンテナーとして使用できます。 静的クラスの主な特徴を以下に示します。 静的メンバーだけが含まれます。 ・ インスタンス化できません。 ・シールされています。 ・インスタンス コンストラクターを含めることはできません。 したがって、静的クラスを作成することと、静的メンバーとプライベート コンストラクターのみを含むクラスを作成することは、基本的に同じです。 プライベート コンストラクターは、クラスのインスタンス化を防ぎます。 静的クラスを使用する利点は、インスタンス メンバーが誤って追加されないことをコンパイラで確認できるという点です。 コンパイラによって、このクラスのインスタンスを作成できないことが保証されます。 (C# 公式リファレンス より) 少し分かりづらさがあるものの、もう分かりつつある。 ...知らない単語を除いて。 一回深呼吸をして ...よし、 まず、静的クラスについて一旦まとめよう 静的クラスとは インスタンスの作成を必要とせず、主にメソッドによるフィールドの演算処理などをまとめたクラス うーむ...うまく言えないし、少しニュアンスが違う気がする。 Qiita的にはよくないのだが、自分がわかる表現をしてみよう 静的クラスとは 主にモジュールのような役割を持つ。 おっけぇい! いわゆるpythonで言うところのモジュールだ!そうだよ! よっしゃ!この勢いで静的メンバーにも触れるぞ! 非静的クラスには、静的メソッド、フィールド、プロパティ、またはイベントを含めることができます。 静的メンバーは、クラスのインスタンスが作成されていない場合でも、クラスで呼び出すことです。 静的メンバーには、必ずインスタンス名ではなくクラス名でアクセスします。 クラスのインスタンスの作成数に関係なく、静的メンバーのコピーは 1 つしか存在しません。 はい!もう完璧よ! 正直なところ、静的ってなんだ?と聞かれたら静的なやつだよとしか答えられない。 というものの、私の語彙力不足と頭の中でいろんな概念が渦巻いているからであろう。 だが、動的と静的について、次の表現ができそうだ。 動的はゲーム内のアイテム、静的はゲームの状態 どういうことかと言うと、 まず、Terrariaというゲームを想像してほしい...のだが 日本人あまりTerraria知らないからなぁ...(ナカマホシイ) Minecraftでいい! Terrariaでもそうなのだが、Minecraftにも雨などの天候が存在する。 MinecraftのModを作ったことはないから知らないが(しかもC#じゃなくてJavaだし) Coreというゲームのシステムを担うクラスがあって、そのメンバとしてbool(Javaだとbooleanかな?)のrainingがあるものとする。 public class Core { public bool raining; } ここで、雨が降っているか降っていないかは、ワールドによって決ま... らねぇじゃねぇかぁあぁぁぁぁあ! あれ絶対チャンクで降るか降らないか設定してそうや! やめやめ! Terrariaでいきます!!!!(Terraria知らない人ごめん) (めっちゃくちゃザックリに言うとMinecraftを2Dにして、いろんなコンテンツ増やして、サバイバルとクリエィティブを合わせたゲーム!(本当はMinecraftとは全く別物)) Terrariaにはゲームの中核を担うクラスが存在し、 そのクラスにrainingというブール値が存在する。 public class Core //実際は"Core"というクラス名ではない { //なんかよく分からないけどすごいコード //... public bool raining; //素晴らしいゲームのコード //... } Terrariaでは雨は1ワールド全体で降る。 Minecraftと同じように、2つ以上のワールドを同時にアクセスはできない。 (一度処理落ちでアプリが複数開いたことがあるのだが、1つを除き全てエラーを吐いた) 1ワールドにつき、雨が降るか降らないか。 ワールドごとに必要に思えるかもしれないが、 プレイヤーが同時にアクセスできるのは1ワールドしかない! つまりrainingフィールドは1つで足りるだろう? 故にこうだ。 public class Core //実際は"Core"というクラス名ではない { //なんかよく分からないけどすごいコード //... public static bool raining; //素晴らしいゲームのコード //... } Q.E.D. さらに言えば、 rainingしかメンバーがないなら クラスも静的でもいいだろう。 public static class Core //実際のコード自体は動的なメンバーも含まれているので無理 { public static bool raining; } Terrariaでは1ゲームで雨が降るか降らないか なので 静的で正確なのだが、 アイテムはどうだろう。 ツルハシ、オノ、剣、ハンマー、杖... アイテムという概念自体は1つだが、 アイテムは複数の種類が存在するため 1ゲームに1つの状態に定まらない。 全てのアイテムのデータに関して、全てフィールドで定義するのか? 否、自作の変数型を作るように、アイテムを定義する。 そっちの方が、分かりやすいし public class Item { public Item(int type) //確かコンストラクタこうじゃないはず { // ... } // public ... // public static ...; // ...... public bool CanUseCondition(Player player) { // 思いつかない } } public class Core { // public ... public const int itemLength = 1784; public static Item[] itemData = new Item[1784]; // public static ...; // ...... public static void DefineItem(int type) // 絶対なさそうな名前を選んだ { for (int k = 0; k < itemLength; k++) { itemData[k] = new Item(k); } //絶対なさそうなメソッド組んだ } } パッと見、配列用意するなら Itemクラスいらなくね?と思うかもしれないが、 クラスはメソッドも追加できる利点がある。 さて、まとめたいところなのだが、 このような例をださないと正直、要約できないのだ。 うーむ... 少し寝不足が... これを終わるまではとりあえず寝ない! いこう! 1アセンブリにおいて、何らかの1つのデータを記録、保持、変更したい場合は静的クラス、もしくは静的メンバを利用する。 つまり動的はその逆、 1アセンブリにおいて、何らかの複数のデータを記録、保持、変更したい場合は動的メンバを利用する。 うーむ...動的に関しては少ししっくりこないのだが、仕方ない。 最後に=>演算子を見て寝るとしよう。 =>演算子 と =演算子 =>演算子に関して公式のリファレンスがあたった。 ラムダ演算子 あぁ~! もういいや こちらに関しては説明しない。 もうこの3文字「ラムダ」が全てを教えてくれた。 ラムダ強し! ...だれだよ 本当は、私無名関数はよくわからず断念していたのだが、 今まで書いてきたModのコード的にもう理解した。 そういうことか。 眺めてみたら もちろん不明瞭なものもあった。 これだけ付け加えておこう public override bool canUse => true; は public override bool CanUse() { return true; } これはちょっとなんか引っ掛かる getとsetどこいったし 終わり 本当に「はっきり」したかというと そうではない。 このタイトルの後には(2/?)とついている。これは、 C#が完全に理解するか、途中であきらめるか、Qiitaに備忘録として書き忘れるまで続く ということだ。 私はTerrariaのコミュニティフォーラム以上にQiitaに出没しない。 十中八九忘れるであろう。 感覚ははっきりした。あとは使ってみてだ。 使わなければ、その感覚は知識に変わらないであろう。 そして知らなかったが、ちょっと気になった単語をメモとして置いておく。 オーバーロード 最後に PythonやLuaを使っていた私から、このC#を見て思ったことをつづろう。 使いづらい言語だな! でも、とても便利そうだ! 以上 第二回C#備忘録 終了します。 おやすみ~ ぐぅ 追記 2021/05/14 getとsetどこいったし ()を見逃していました 誤認識についてのコメント(ありがとうございます)を含め、 ↓第三回C#備忘録でまとめ治してます
  • このエントリーをはてなブックマークに追加
  • Qiitaで続きを読む

WPF-MVVM開発手法

誰かにMVVMを説明するとき用のメモです。MVVMを採用するメリットに関してはかなり局所的な内容になっているかもしれません。詳しくは下記の説明等を参照ください。 参考:https://qiita.com/yuutetu/items/ea175b73e1dbbfd355db MVVM概要 Model(データ入出力管理)、View(UI処理)、ViewModel(ModelデータをUIデータに変換、及び、操作コマンドより対応したModel処理を呼び出す)で構成する開発手法です。 MVVM開発手法を採用するメリットとしては、下記が挙げられます。 UIとビジネスロジック(データ・処理)が分離される 具体的には、Formアプリケーションの一般的な構成では、UIロジックがModelのプロパティを直接参照しますが、この手法の場合、UI処理開発時にModelが必要となるか、UI処理の中に直接ビジネスロジックが存在することとなります。 一方、MVVM開発手法では、例えばデータバインドを用いると、UIからは直接Modelのインスタンスを参照せず、対応するプロパティの名前でバインド定義する為、View開発時にModelのインスタンスは不要です。 UIロジックとビジネスロジックを分離することで、コードの可読性が向上する。 データバインドの例 <実行イメージ> ボタンを押下するとテキスト内の数値がカウントアップ View処理:MainWindow.xaml <Window ~省略~ /> <Grid> <TextBox Text="{Binding Path=propString.Value}" /> <Button Content="Button" Command="{Binding ShowMessageButtonCommand}"/> </Grid> </Window> 3. ViewModel処理:MainWindowViewModel.cs using Prism.Mvvm; using Reactive.Bindings; namespace prism_test.ViewModels { public class MainWindowViewModel : BindableBase { /* テキストボックスに表示する文字列 */ public ReactiveProperty<string> propString { get; set; } = new ReactiveProperty<string>(); /* ボタン押下時のコマンドプロパティ */ public ReactiveCommand ShowMessageButtonCommand { get; } /* コンストラクタ */ public MainWindowViewModel() { propString.Value = "0"; this.ShowMessageButtonCommand = new ReactiveCommand().WithSubscribe(this.buttonpush); } /* ボタン押下時のコマンド実体 */ public void buttonpush() { propString.Value = (int.Parse(propString.Value) + 1).ToString(); ; } } }
  • このエントリーをはてなブックマークに追加
  • Qiitaで続きを読む

動的とか静的とか、プロパティとかメソッドとか、そういうのをはっきりさせたいだけ (1/?)

smileBASICから始まり pythonを学校で習い luaで遊んで C#を知る 私は今、人生の岐路にいる if ~ elseif ~ else ~の処理直前と言ったところか とにかく、プログラミングを本気でやるのなら 動的とか静的とか プロパティとかメソッドとか 独学で感覚的に手に入れた知識をはっきりさせたいと思ったのだ そもそも 今しがた列挙したプログラミング言語、 smile Basic, python, luaには存在しない(または、存在するのかもしれないがそのレベルまで到達できなかったのか)が C#には存在する概念がある... C#を初めて触れた時、今までやってきた言語とは全くの別物だと感じた。 そう、予約語の多さ、型の多さ、そして... 修飾語である。 うーむ、といあえずこの言葉があっているのかいささか心配なので検索にかける。 修飾子だった。早速、自分がどのくらい知識がないか分かった。 ともあれ、pyhtonにクラスやクラスの継承の概念は存在するのだが、 public, private, internal, protectedや abstract, virtual, sealedなどなど... 修飾子の正確な使い方を知らない。 そして動的、静的とか、 public <型> <変数名>;とpublic <型> <プロパティ名> { get; set; }とか =と=>とか 全然理解できていない。 なんとなくでしか分からない。 このなんとなくをはっきりさせたいのだ。 とりあえず 疑問を解決するにあたって、とりあえず、今の認識や感覚を列挙していこう。 まず、そもそもクラスとは何か、から 今の私の認識は「自作の型をつくるもの」である。 pythonの時からそのような認識である。 もしかしたらこの時点でもう終わっているかもしれない。。。 とりあえず、まず、どんどん今の認識を挙げていこう 動的(変数?) -> インスタンスを作成しないとアクセスできない変数 静的(変数?) -> インスタンスを作成しなくてもアクセスできる変数 プロパティ -> public <型> <名前> { get; set; }のやつ。この単語自体はVisual Studioで知った インスタンス -> クラス内の動的変数のこと メソッド -> 関数。 この単語もVisual Studioで知った public -> どのファイルからでもアクセスできる private -> アクセスできない 多分継承すればアクセスできるはず internal -> publicとは違うんだろうけど詳しくは分からない。アクセスできるっぽかったから protected -> これを使ったことはないし、知らないが、見たことはあるので挙げてみた abstract -> 継承元のクラスにつけるやつ。確か抽象クラスって名前だった virtual -> overrideしたいメソッドにつけるやつ static -> 静的にしたいときにつけるやつ sealed -> 継承禁止 override -> override 継承クラスにおけるメソッド、プロパティの書き換え =>演算子 -> プロパティにおける=の役割 とりあえずあやふやな知っているものを列挙した ここでVisual Studioを開いて、今作っているModのプロジェクト内で列挙していない、あやふやなものがないか確かめる。 そう、私の初めてのC#はModなのだ。 TerrariaのMod。tModLoaderだ。 Example Modを元になんとなくでしか理解できなかった。 そうだ、<T>()ってなんだろう。あと、System.collections.genericのList<>が列挙型([]のこと)より遥かに使いやすくて、Modでなければ結構使ってしまうのだが、 それははたしていいことなのか? 一応、namespace(名前空間)もはっきりさせておこう。 だいたい調べなければならないものが分かった。 はっきりさせていく はっきりさせる方法は、まず、とにかく調べることだ。 あとは実際に使って、少しいじって、確かめていくしかない。 本は期待が薄い。そもそも近くの本屋にはそういったことよりも、入門書ぐらいしかない。 まずは後で挙がった、一応はっきりさせようと思ったnamespaceからだ。 namespace (名前空間) 検索候補からいろんなプログラミング言語が表示された(phpとか) そして、なんとwikipediaが最初に出てきた。 名前空間(なまえくうかん)はNamespaceの訳語で、名前の集合を分割することで衝突の可能性を低減しつつ参照を容易にする概念である。 (wikipedia 名前空間 より) このくらいは知っている。 例えばMinecraftのTNTのレシピを追加しようとする ↑の例を読みたい人向け バニラのレシピはおなじみ火薬×5と砂×4のあの形 そのレシピを担うファイル名は確か、tnt.jsonである。 ここで、データパックで火薬×9でもTNTが作れるレシピを追加したい。 そして、ファイル名をtnt.jsonにする 仮に名前空間という概念が存在しない場合、 バニラとデータパックのtnt.jsonのどちらに従えばいいのか分からずMinecraftがTNT2147483647個分の大爆発を起こす 仮にロードできたとしても、Minecraftかデータパックのどちらかのレシピが優先され、 2通りのレシピではなく、どちらかのレシピしか使えない。 バニラとModやMod同士の競合を防ぐため、どのコンテンツのものなのかを表すために名前空間が存在する。 名前空間はソースコード上で冗長な命名規則を用いなくても名前の衝突が起こらないようにし、しかもそれを容易に記述できるようにするためだけの概念であり、普通はそれ以上の意味は持っていない (wikipedia 名前空間 #プログラミング より) ただ単に競合しないためだけの概念らしい。 認識はあっていたっぽい。 public, private, internal, protectedなど だいたいなんでも最初につけるが、そもそもこいつらの名前って何なんだ? とりあえずpublic private internal protected 名前と検索をかける Microsoft公式のリファレンスが引っ掛かった。 アクセス修飾子というらしい。 そしてそこにそのまま解説があった。 要約しよう...と思ったのだが、アセンブリという聞いたり見たりしたことはあるが、あやふやな認識の単語が出てきた。 まずいったん置いておいて 先にクラスについて理解しておこう クラス クラスと構造体は、どちらも基本的にはデータと動作のセットを 1 つの論理単位としてカプセル化するデータ構造です。 データとビヘイビアーはクラス、構造体、またはレコードの "メンバー" です。この記事で後述するように、そのメソッド、プロパティ、イベントなどが含まれます。 (C# 公式リファレンスより) でたよ。 その単語を調べると、さらに分からない単語が増えていく現象!!! まるでwikipediaで量子力学の単語の解説を適当に漁って見てるかのようだ! この現象に名前をつけてやりたいほど嫌いだ。 さて、名前は... じゃなくて! はっきりさせるためには、確かに全ての単語を理解した方がいいのかもしれない。 しかし、私には時間がないのだよ、諸君。 とりあえず、このまま続きを読もう。 クラスまたはレコードは参照型です。 型のオブジェクトが作成されると、オブジェクトが割り当てられている変数にはそのメモリへの参照だけが設定されます。 オブジェクト参照が新しい変数に割り当てられると、新しい変数は元のオブジェクトを参照します。 いずれの変数も同じデータを参照しているため、1 つの変数に加えられた変更は他の変数にも反映されます。 (C# 公式リファレンスより) レコードってんのは聞いたことも見たこともない全く分からないものので、そこは飛ばす。 今回はっきりさせるのは認識があやふやな部分だ。 使う機会がある時に回そう。 なぜだろう、ほとんどが知っている単語のはずなのに、全く頭に入ってこない。 __init__.pyの書き方を思い出すなぁ... 一般に、クラスは、より複雑な動作、つまりクラス オブジェクトの作成後に変更されることを意図されたデータをモデル化するために使用されます。 構造体は、主として構造体の作成後に変更されることを意図しないデータを含む、小規模なデータ構造に最適です。 (C# 公式リファレンスより) ああああああ!もう!やめてくれ!!!!!!! 例 やはり手っ取り早いのはプログラムの例をみることだろう。 コメントから早速分かったことがある。 まず、<アクセス修飾子> class <クラス名> { }の{ }内に書かれるものは、 そのクラスのメンバーだ。 そして<アクセス修飾子> <型> <名前> { get; set; }はプロパティと言い、 関数だと思っていたものはメソッドと言うらしい。 この認識は間違っていなかった。(メンバーという単語は知らなかったが) そして、少し衝撃を受けた。 // Create an object of type CustomClass. CustomClass custClass = new CustomClass(); まじか 今まで<型> <変数名> = new <型>();のことを インスタンスの作成だと思っていた。 正しくはオブジェクトの作成らしい。 と思ったのだが、 上を見てみると、 CustomClass のインスタンス (オブジェクト) が作成され なんやねん。 既知(であると思っている)だったので挙げなかったが 全ての型はobjectクラス(object型)が基本クラスであるというのがある。 そのオブジェクトから来ているっぽそうだ。 <型> <変数名> = new <型>();のことをインスタンスの作成と言うことについて 認識は正しかったようだ。 さらに例から下にスクロールし、リファレンスを読み続けると、 私のあやふやな認識の答えが、初めて見た分からない単語とともに書いてあった。 早速並べていきたいところだが、一度戻ろうではないか、 アクセス修飾子について アセンブリ いろいろ分かりそうになったものの、一旦、ざっとクラスのリファレンスを読んでみたが、 アクセス修飾子を理解しようとしてぶつかった壁、 問題のアセンブリという単語は1つも出てこなかったのである。 アセンブリごときも知らねぇお前にC#をやる資格はねぇっていうほど基本中の基本なのかな? そんな基本ならば入門書にあるんじゃないかと思って、買ってあった本を読んでみた。 なんと索引にアセンブリ名という単語があったものの。 やはり入門書はダメだ。私の知識を潤すには、あまりにも水が入っていなかった。 それどころかスポンジか?私の知識を乾かしていく。 意味わかんないことを言い出したので、インターネットに逃げる。 c# アセンブリとは 検索 なんと一番上にヒットしたのはqiitaの記事であった...のだが 残念ながら私の求める答えではなかった。 そしてまたヒットした、公式リファレンス アセンブリは、.NET ベースのアプリケーションの配置、バージョン管理、再利用、アクティベーション スコープ、およびセキュリティ権限の基本単位です。 アセンブリは、相互に連携して 1 つの論理的な機能単位を形成するように構築された型やリソースの集合です。 アセンブリは、実行可能 ( .exe) ファイルまたはダイナミック リンク ライブラリ ( .dll) ファイルの形を取る、.NET アプリケーションの構成要素です。 それらは、型の実装に関して必要な情報を共通言語ランタイムに提供します。 期待を裏切らないですね。全く分かんねぇ。 だが、適当にリファレンスをスクロールして、はっと気づいた。 TerrariaのModのプロジェクトを再度開く。 そして、依存関係を展開してみた。 ビンゴだ。 そこにはアセンブリがあり、さらに展開するとTerrariaがあった。 全て腑に落ちた。多分。 だがしかし、納得した、でやめてはいけない。 本当に理解したのなら、自分の言葉で説明できるはずだ。 実践しよう。 アセンブリとは .exeファイルとか.dllファイルのこと~ おい、これじゃあさっきチラ見したQiitaの記事と変わんねぇじゃねぇか 確かに簡潔にまとめることは必要だが、 これは備忘録だ! 一回これで納得していないんだから 自分が納得できる言葉を選べよ!!! 後、Qiita的にも!! 言いなおします アセンブリとは コンパイルされてできた、アプリケーション(.exeファイル)や、Dynamic Link Library(.dllファイル)のことを指す 少なくともコードのことではない うん?あんまり変わってなくね? ...まぁ、これが正しいのならアクセス修飾子の説明が分かるはずだ。 読んでみる。 おぉ!読める読める読めるぞぉ!! 分かったのはいいのだが、あまりにも、蛇足が多すぎて 記事がこんな長くなってしまったぞ... 次回の記事の最初にまとめるとして、 私は早速だが 動的とか静的とか、プロパティとかメソッドとか、そういうのをはっきりさせたいだけ (2/?)を書き始めるぞ 書き次第投稿するぜ! じゃぁな!
  • このエントリーをはてなブックマークに追加
  • Qiitaで続きを読む

[C#]対象文字列内の指定した文字列以降を削除する。

対象文字列から指定した文字列以降を削除する関数が標準ではなかったので、自作しました。 /// <summary> /// 対象文字列から指定した文字列以降を削除します。 /// ただし、対象文字列内に指定した文字列が存在しなかった場合、対象文字列をそのまま返却します。 /// </summary> /// <param name="str">対象文字列</param> /// <param name="removeStr">指定文字列</param> /// <returns>対象文字列から指定文字列を削除した文字列</returns> public static string RemoveRight(string str, string removeStr) { var length = str.IndexOf(removeStr); if(length < 0) { return str; } return str.Substring(0, length); } 注意点は、コメント記載の通りです。
  • このエントリーをはてなブックマークに追加
  • Qiitaで続きを読む