20211016のC#に関する記事は4件です。

C#とNode.jsを連携する

C#とNode.jsを連携する EdgeJsを利用する EdgeJsを利用しNode.jsからC#通信ができます。その逆もできます。.net4.6のみ。まだ.netCoreには未対応のようです。 Node.js側 const edge = require('edge-js'); const helloWorld = edge.func('cs', function () {/* async (input) => { return ".NET Welcomes " + input.ToString(); } */}); helloWorld('Node.js', function (error, result) { if (error) throw error; console.log(result); }); C#側 NodeJsからC#のメソッドを実行しデーターを渡します edgeJsライブラリ 現在は更新されていません。メンテナンスを引き継いだプロジェクトが別にあります。 npm install edge edgeで読み込む const edge = require('edge'); フォークされたもの 現在も保守されている npm install edge-js edgeの後ろに-jsを付ける const edge = require('edge-js'); 説明 Node.jsにインストールされたモジュールを表示する npm list npm -v 環境構築 DockerFileの内容をみて必要なライブラリをインストールします。 Monoは必要ないです。 RiderのエディタからedgeJSモージュールをインストールする npm install後にエディタ上で 波線をクリックするとnode_moduleフォルダが生成され、edgeモジュールがインストールされる 動作に必要な設定 コマンド 有無 値 備考 EDGE_USE_CORECLR 必須 1 .NetCore用のedgeモジュールを利用する EDGE_NATIVE 必須 edge-js/build/Release/edge_coreclr.node 相対パスで設定する 絶対パスだとエラーが出る EDGE_DEBUG オプション 1 デバッグ表示 EDGE_APP_ROOT オプション Publishフォルダへのパス 外部Dll Nugetライブラリを利用する時に使う 相対パスで設定する 環境変数の設定の仕方 ターミナルから設定し実行する EDGE_NATIVE=edge-js/build/Release/edge_coreclr.node EDGE_USE_CORECLR=1 EDGE_DEBUG=1 node test.js RiderのNode.js設定画面から設定する EnviromentValueで設定します。 JSプログラムの中に書く var path = require('path'); const baseNetAppPath = path.join(__dirname, '/bin/Release/netstandard2.0/publish/'); process.env.EDGE_USE_CORECLR = 1; process.env.EDGE_DEBUG=1; process.env.EDGE_NATIVE='edge-js/build/Release/edge_coreclr.node'; //process.env.EDGE_APP_ROOT = baseNetAppPath; var edge = require('edge-js'); サンプルを実行 node_modules/edge-jsフォルダの中にSampleファイルがあり、いろいろ試せます。 デバッグ 3種類 C#側のデバッグ 実行ボタンでデバッグ エラーが緑文字で出る。C#側のCSファイルビルド状態、Dllファイルが読み込まれたかどうか Dllファイルがあるかアセンブリが読み込まれたかどうかなどいろいろ確認できます。 Node.js側のデバッグ 虫のアイコンでNode.js側がデバッグできます。Js側でブレイクポイントなどをしてデバッグします。 edge.jsを追加するとブレイクポイントでデバッグできるようになる ブレイクポイント追加する Debugger ConsoleとProcess Consoleでエラーログを見ることができます。 よく出るエラー Uncaught TypeError: edge.initializeClrFunc is not a function 一見JavaScript側のエラーのように見えるがC#側のエラーであることが多いです。C#側を確認するとよいです。 Error occurred during CoreCLR initialization node:internal/modules/cjs/loader:1168 return process.dlopen(module, path.toNamespacedPath(filename)); パスのどれかに絶対パスが含まれていると出るエラーになります。相対パスに直すと通ります。 環境変数の設定などで起きる ダイナミックライブラリの中に外部DllやNugetライブラリを利用する設定 プロジェクトフォルダに以下の値を追加する。 <Project Sdk="Microsoft.NET.Sdk"> <PropertyGroup> <TargetFramework>netcoreapp2.0</TargetFramework> <PreserveCompilationContext>true</PreserveCompilationContext> <CopyLocalLockFileAssemblies>true</CopyLocalLockFileAssemblies> </PropertyGroup> <ItemGroup> <Reference Include="EdgeJs"> <HintPath>..\..\node_modules\edge-js\lib\bootstrap\bin\Release\netcoreapp1.1\EdgeJs.dll</HintPath> <Private>true</Private> </Reference> </ItemGroup> </Project> .NET5のところをクリックしAdd FromからもDllを追加できます Publishを設定する Publishを実行すると依存ファイル(dllなど)、Nugetから入れたDLLをすべて同じフォルダに書き出せます。 Publishフォルダへパスを通す。相対がよいです。 process.env.EDGE_APP_ROOT = ''; Nugetから必要ライブラリをインストール Microsoft.NETCore.DotNetHost Microsoft.NETCore.DotNetHostPolicy このライブラリには既に依存ファイルが設定されています 現在の状態 Dllファイルへのアクセス サンプルファイルのDllへはアクセスができましたが、DLLプログラムのの中にNugetのライブラリを入れると読み込まれない状態です。 DapperやMicoroSoftのSqliteライブラリなどが読み込まれない。NewtonSoftのJesonのライブラリは読み込め使えました。 EDGE_APP_ROOTにPublishまでの相対パスを通すとエラーが起きます。 読み込み方法がわかる方ご教授ください。 参考 EdgeJsをC#のソースコードからデバッグするに続く
  • このエントリーをはてなブックマークに追加
  • Qiitaで続きを読む

WindowsFomのpicureBox上での範囲指定

概要 マウスを使ってpictureBox上で範囲指定するプログラムです。 指定した範囲を線で表示します。 マウスの3つのイベントを利用してます。 作業手順 ①新規プロジェクト作成 Windows フォームアプリケーション(.Net Framework)を選択 名前:SelectArea ②pictureBoxを作成 ・Name:_Picture ・BackColorを設定:pictureBoxの範囲を見える化 ③Form1.cssに下記のように記述 using System; using System.Drawing; using System.Windows.Forms; namespace SelectArea { public partial class Form1 : Form { Rectangle _Rect; Point _PosStart; bool _IsSelect = false; public Form1() { InitializeComponent(); } /// <summary> /// マウスボタンが押された時のイベント /// </summary> /// <param name="sender"></param> /// <param name="e"></param> void OnMouseDown(object sender, MouseEventArgs e) { if (e.Button == MouseButtons.Left) { // 範囲選択開始 _IsSelect = true; // 開始位置をセットする _PosStart.X = e.X; _PosStart.Y = e.Y; } } /// <summary> /// マウスポインターが移動した時のイベント /// </summary> /// <param name="sender"></param> /// <param name="e"></param> void OnMouseMove(object sender, MouseEventArgs e) { if (e.Button == MouseButtons.Left && _IsSelect) { // 四角形を描く DrawRect(e.X, e.Y); } } /// <summary> /// マウスボタンが離された時のイベント /// </summary> /// <param name="sender"></param> /// <param name="e"></param> void OnMouseUp(object sender, MouseEventArgs e) { if ((e.Button == MouseButtons.Left) && (_IsSelect == true)) { //四角形を描く DrawRect(e.X, e.Y); // 範囲選択終了 _IsSelect = false; } } /// <summary> /// 四角形の描画 /// </summary> /// <param name="clickX"></param> /// <param name="clickY"></param> void DrawRect(int clickX, int clickY) { // PictureBoxの範囲内に収める if (clickX < 0) clickX = 0; else if (clickX >= _PictureBox.Width) clickX = _PictureBox.Width - 1; if (clickY < 0) clickY = 0; else if (clickY >= _PictureBox.Height) clickY = _PictureBox.Height - 1; // サイズをセットする _Rect.Width = Math.Abs(clickX - _PosStart.X); _Rect.Height = Math.Abs(clickY - _PosStart.Y); // 開始位置より小さい場合は座標を入れ替える _Rect.X = (clickX >= _PosStart.X) ? _PosStart.X : clickX; _Rect.Y = (clickY >= _PosStart.Y) ? _PosStart.Y : clickY; // 青の破線に設定する Pen pen = new Pen(Color.Blue, 1); pen.DashStyle = System.Drawing.Drawing2D.DashStyle.DashDot; // 描画領域をリフレッシュする _PictureBox.Refresh(); // 選択範囲を描画する Graphics g = _PictureBox.CreateGraphics(); g.DrawRectangle(pen, _Rect); g.Dispose(); } } } ④マウス関連のイベントを登録
  • このエントリーをはてなブックマークに追加
  • Qiitaで続きを読む

Unity設計入門:第1章「ModelとViewの分離」・実践編

Unity設計入門の目次はこちら はじめに 理論編では、ModelとViewを分離することの利点について解説しました。実践編では以下のようなアプリケーションを作って、ModelとViewが分離できていない状態の欠点を確認していきます。 作成するもの 1から9の数字を好きな順番で選択することが出来ます。 n番目にnの数字を選択すると、背景が赤くなります。 n番目にn以外の数字を選択すると、背景が青くなります。 n個の数字を選択すると、「赤色の数字の数*青色の数字の数」の値が左上にスコアとして表示されます。 ヒエラルキービュー ゲームオブジェクト1~9は単なるuGUIのImageとTextです。また、Scoreは単なるuGUIのTextです。 それぞれの数字の大きさは200px*200px、数字と数字の間隔は100px、画面全体の大きさは1920px*1080pxです。 ViewとModelが密結合しているコード さて、上の仕様を実現するために以下のようなコードを書いたとしましょう。このコードがCanvasにアタッチされます。 そこまで詳しく読み込まなくても記事の理解に差し支えないので、軽く流れを理解する程度で読み進めてください。 NineNumberManager.cs public class NineNumberManager : MonoBehaviour { Image[] buttons; Text score; List<int> selectedList; void Start() { var one = transform.Find("1").GetComponent<Image>(); var two = transform.Find("2").GetComponent<Image>(); var three = transform.Find("3").GetComponent<Image>(); var four = transform.Find("4").GetComponent<Image>(); var five = transform.Find("5").GetComponent<Image>(); var six = transform.Find("6").GetComponent<Image>(); var seven = transform.Find("7").GetComponent<Image>(); var eight = transform.Find("8").GetComponent<Image>(); var nine = transform.Find("9").GetComponent<Image>(); buttons = new Image[] { one, two, three, four, five, six, seven, eight, nine }; score = transform.Find("Score").GetComponent<Text>(); selectedList = new List<int>(); } private void Update() { if (Input.GetMouseButton(0)) { Vector2 mousePosition = Input.mousePosition; if (mousePosition.x <= 760 && mousePosition.x >= 560 && mousePosition.y <= 940 && mousePosition.y >= 740) { OnClick(1); } if (mousePosition.x <= 1060 && mousePosition.x >= 860 && mousePosition.y <= 940 && mousePosition.y >= 740) { OnClick(2); } if (mousePosition.x <= 1360 && mousePosition.x >= 1160 && mousePosition.y <= 940 && mousePosition.y >= 740) { OnClick(3); } if (mousePosition.x <= 760 && mousePosition.x >= 560 && mousePosition.y <= 640 && mousePosition.y >= 440) { OnClick(4); } if (mousePosition.x <= 1060 && mousePosition.x >= 860 && mousePosition.y <= 640 && mousePosition.y >= 440) { OnClick(5); } if (mousePosition.x <= 1360 && mousePosition.x >= 1160 && mousePosition.y <= 640 && mousePosition.y >= 440) { OnClick(6); } if (mousePosition.x <= 760 && mousePosition.x >= 560 && mousePosition.y <= 340 && mousePosition.y >= 140) { OnClick(7); } if (mousePosition.x <= 1060 && mousePosition.x >= 860 && mousePosition.y <= 340 && mousePosition.y >= 140) { OnClick(8); } if (mousePosition.x <= 1360 && mousePosition.x >= 1160 && mousePosition.y <= 340 && mousePosition.y >= 140) { OnClick(9); } } } void OnClick(int index) { if (selectedList.Contains(index)) { return; } if ((selectedList.Count + 1) == index) { buttons[index - 1].color = Color.red; } else { buttons[index - 1].color = Color.blue; } selectedList.Add(index); if (selectedList.Count == 9) { WriteScore(); } } void WriteScore() { score.text = $"{CountAccord() * CountDiscord()}"; } int CountAccord() { return buttons.Count( numberPanel => numberPanel.color == Color.red ); } int CountDiscord() { return buttons.Count(numberPanel => numberPanel.color == Color.blue ); } } それでは、このコードを例に、ModelとViewの密結合がいかに悪影響であるかを見ていきます。 シーン間でデータが共有できない 「スコア」や「どのが選択されたのか」という、いかにも重要でほかのシーンでも流用できそうなデータが、Monobehaviourのフィールドに保存されています。これによって、シーンのアンロードとともに重要データが消えてしまうために、複数画面を作ろうとした時に困ってしまうのです。 また、スコアに関しては、保存先がただの文字列になっています。別のコンポーネントがスコアを使いたくなったときにはわざわざどこのコンポーネントに保存されているのかを探して、GameObject.Findを呼んで、そこからint.Parseを呼ばなければなりません。ViewはUnityの仕組みに従ってたくさんのゲームオブジェクトやMonobehaviourに分散するので、ViewがModelのデータを持つということは、データが分散して保存されて一覧性や統一性、実装の柔軟性が下がるということなのです。 デザインが変わったら連鎖的に不具合が発生する。その1 例えば、ボタンのサイズを変更したとします。この場合、ボタンの大きさに依存したOnClickを呼び出している以下の部分は動かなくなります。 if (mousePosition.x <= 1060 && mousePosition.x >= 860 && mousePosition.y <= 340 && mousePosition.y >= 140) { OnClick(8); } つまり、デザインを変更するときには常に全部が壊れることに怯えないといけないのです。これは、デザイナーが気軽にデザインを変えることが出来ず、作業分担が難しくなるということを意味します。 デザインが変わったら連鎖的に不具合が発生する。その2 このコードにおいて「n番目にnが選択された」という情報は、Viewに記録されています。なぜなら、「同じなら赤、違ったら青」というデザイン上の仕様に基づいて、色がColor.Redであるなら、n番目にnが選択されていて、Color.blueならn番目にn以外が選択された、という判定をしているからです。 int CountAccord() { return buttons.Count( numberPanel => numberPanel.color == Color.red ); } int CountDiscord() { return buttons.Count(numberPanel => numberPanel.color == Color.blue ); } これもまた、デザインが変わると動かなくなるコードですね。色の設定部分を赤から黄色に変えたらそれだけで動かなくなります。つまり、デザインを変えることに怯えないといけません。 メインロジックを検証できない その1 「スコア計算」というメイン部分が以下のように、文字列埋め込みの内部に入っている状況です。 つまり、スコア計算の動作確認をするには、プログラムをほとんど完成させなければなりません。 なぜなら、CountAccord()やCountDiscord()はViewに属するそれぞれの数字の背景色を数えているからです。つまり、Viewが完成するまでModelが検証できないということです。一般に、テストが遅くなればなるほどバグの特定は難しくなります。したがって、これは悪い設計です。 score.text = $"{CountAccord() * CountDiscord()}"; メインロジックが検証できない その2 キー入力からOnClick関数を呼び出す部分では、Inputに依存した処理を書いています。 if (Input.GetMouseButton(0)) { Vector2 mousePosition = Input.mousePosition; if (mousePosition.x <= 760 && mousePosition.x >= 560 && mousePosition.y <= 940 && mousePosition.y >= 740) { OnClick(1); } この処理は、どの場合にどの関数を呼び分けるかという、本来Viewには関係ない(すなわち、Unityがなくても成立する)ような処理であるのにもかかわらず、Inputがないとテストが出来ません。これがどういうことかと言うと、いちいちUnityを起動してボタンを押さないとテストできません。非常に使いにくい設計です。 おわりに 理論的にも実践的にも、ModelとViewの分離の必要性について、実感を持ってもらえると嬉しいです。 第1章でわかったこととしては、ModelとViewが分離できると嬉しい!ということだけであり、現実的にどのように分離していくのかについては第2章以降で具体的に議論を進めていきます。
  • このエントリーをはてなブックマークに追加
  • Qiitaで続きを読む

Unity設計入門:第1章「ModelとView」・実践編

Unity設計入門の目次はこちら はじめに 理論編では、ModelとViewを分離することの利点について解説しました。実践編では以下のようなアプリケーションを作って、ModelとViewが分離できていない状態の欠点を確認していきます。 作成するもの 1から9の数字を好きな順番で選択することが出来ます。 n番目にnの数字を選択すると、背景が赤くなります。 n番目にn以外の数字を選択すると、背景が青くなります。 n個の数字を選択すると、「赤色の数字の数*青色の数字の数」の値が左上にスコアとして表示されます。 ヒエラルキービュー ゲームオブジェクト1~9は単なるuGUIのImageとTextです。また、Scoreは単なるuGUIのTextです。 それぞれの数字の大きさは200px*200px、数字と数字の間隔は100px、画面全体の大きさは1920px*1080pxです。 ViewとModelが密結合しているコード さて、上の仕様を実現するために以下のようなコードを書いたとしましょう。このコードがCanvasにアタッチされます。 そこまで詳しく読み込まなくても記事の理解に差し支えないので、軽く流れを理解する程度で読み進めてください。 NineNumberManager.cs public class NineNumberManager : MonoBehaviour { Image[] buttons; Text score; List<int> selectedList; void Start() { var one = transform.Find("1").GetComponent<Image>(); var two = transform.Find("2").GetComponent<Image>(); var three = transform.Find("3").GetComponent<Image>(); var four = transform.Find("4").GetComponent<Image>(); var five = transform.Find("5").GetComponent<Image>(); var six = transform.Find("6").GetComponent<Image>(); var seven = transform.Find("7").GetComponent<Image>(); var eight = transform.Find("8").GetComponent<Image>(); var nine = transform.Find("9").GetComponent<Image>(); buttons = new Image[] { one, two, three, four, five, six, seven, eight, nine }; score = transform.Find("Score").GetComponent<Text>(); selectedList = new List<int>(); } private void Update() { if (Input.GetMouseButton(0)) { Vector2 mousePosition = Input.mousePosition; if (mousePosition.x <= 760 && mousePosition.x >= 560 && mousePosition.y <= 940 && mousePosition.y >= 740) { OnClick(1); } if (mousePosition.x <= 1060 && mousePosition.x >= 860 && mousePosition.y <= 940 && mousePosition.y >= 740) { OnClick(2); } if (mousePosition.x <= 1360 && mousePosition.x >= 1160 && mousePosition.y <= 940 && mousePosition.y >= 740) { OnClick(3); } if (mousePosition.x <= 760 && mousePosition.x >= 560 && mousePosition.y <= 640 && mousePosition.y >= 440) { OnClick(4); } if (mousePosition.x <= 1060 && mousePosition.x >= 860 && mousePosition.y <= 640 && mousePosition.y >= 440) { OnClick(5); } if (mousePosition.x <= 1360 && mousePosition.x >= 1160 && mousePosition.y <= 640 && mousePosition.y >= 440) { OnClick(6); } if (mousePosition.x <= 760 && mousePosition.x >= 560 && mousePosition.y <= 340 && mousePosition.y >= 140) { OnClick(7); } if (mousePosition.x <= 1060 && mousePosition.x >= 860 && mousePosition.y <= 340 && mousePosition.y >= 140) { OnClick(8); } if (mousePosition.x <= 1360 && mousePosition.x >= 1160 && mousePosition.y <= 340 && mousePosition.y >= 140) { OnClick(9); } } } void OnClick(int index) { if (selectedList.Contains(index)) { return; } if ((selectedList.Count + 1) == index) { buttons[index - 1].color = Color.red; } else { buttons[index - 1].color = Color.blue; } selectedList.Add(index); if (selectedList.Count == 9) { WriteScore(); } } void WriteScore() { score.text = $"{CountAccord() * CountDiscord()}"; } int CountAccord() { return buttons.Count( numberPanel => numberPanel.color == Color.red ); } int CountDiscord() { return buttons.Count(numberPanel => numberPanel.color == Color.blue ); } } それでは、このコードを例に、ModelとViewの密結合がいかに悪影響であるかを見ていきます。 シーン間でデータが共有できない 「スコア」や「どのが選択されたのか」という、いかにも重要でほかのシーンでも流用できそうなデータが、Monobehaviourのフィールドに保存されています。これによって、シーンのアンロードとともに重要データが消えてしまうために、複数画面を作ろうとした時に困ってしまうのです。 また、スコアに関しては、保存先がただの文字列になっています。別のコンポーネントがスコアを使いたくなったときにはわざわざどこのコンポーネントに保存されているのかを探して、GameObject.Findを呼んで、そこからint.Parseを呼ばなければなりません。ViewはUnityの仕組みに従ってたくさんのゲームオブジェクトやMonobehaviourに分散するので、ViewがModelのデータを持つということは、データが分散して保存されて一覧性や統一性、実装の柔軟性が下がるということなのです。 デザインが変わったら連鎖的に不具合が発生する。その1 例えば、ボタンのサイズを変更したとします。この場合、ボタンの大きさに依存したOnClickを呼び出している以下の部分は動かなくなります。 if (mousePosition.x <= 1060 && mousePosition.x >= 860 && mousePosition.y <= 340 && mousePosition.y >= 140) { OnClick(8); } つまり、デザインを変更するときには常に全部が壊れることに怯えないといけないのです。これは、デザイナーが気軽にデザインを変えることが出来ず、作業分担が難しくなるということを意味します。 デザインが変わったら連鎖的に不具合が発生する。その2 このコードにおいて「n番目にnが選択された」という情報は、Viewに記録されています。なぜなら、「同じなら赤、違ったら青」というデザイン上の仕様に基づいて、色がColor.Redであるなら、n番目にnが選択されていて、Color.blueならn番目にn以外が選択された、という判定をしているからです。 int CountAccord() { return buttons.Count( numberPanel => numberPanel.color == Color.red ); } int CountDiscord() { return buttons.Count(numberPanel => numberPanel.color == Color.blue ); } これもまた、デザインが変わると動かなくなるコードですね。色の設定部分を赤から黄色に変えたらそれだけで動かなくなります。つまり、デザインを変えることに怯えないといけません。 メインロジックを検証できない その1 「スコア計算」というメイン部分が以下のように、文字列埋め込みの内部に入っている状況です。 つまり、スコア計算の動作確認をするには、プログラムをほとんど完成させなければなりません。 なぜなら、CountAccord()やCountDiscord()はViewに属するそれぞれの数字の背景色を数えているからです。つまり、Viewが完成するまでModelが検証できないということです。一般に、テストが遅くなればなるほどバグの特定は難しくなります。したがって、これは悪い設計です。 score.text = $"{CountAccord() * CountDiscord()}"; メインロジックが検証できない その2 キー入力からOnClick関数を呼び出す部分では、Inputに依存した処理を書いています。 if (Input.GetMouseButton(0)) { Vector2 mousePosition = Input.mousePosition; if (mousePosition.x <= 760 && mousePosition.x >= 560 && mousePosition.y <= 940 && mousePosition.y >= 740) { OnClick(1); } この処理は、どの場合にどの関数を呼び分けるかという、本来Viewには関係ない(すなわち、Unityがなくても成立する)ような処理であるのにもかかわらず、Inputがないとテストが出来ません。これがどういうことかと言うと、いちいちUnityを起動してボタンを押さないとテストできません。非常に使いにくい設計です。 おわりに 理論的にも実践的にも、ModelとViewの分離の必要性について、実感を持ってもらえると嬉しいです。 第1章でわかったこととしては、ModelとViewが分離できると嬉しい!ということだけであり、現実的にどのように分離していくのかについては第2章以降で具体的に議論を進めていきます。
  • このエントリーをはてなブックマークに追加
  • Qiitaで続きを読む