- 投稿日:2020-07-07T17:58:51+09:00
今後習得したい技術
★目次
1.自己紹介
2.マスターしたい技術
3.現在の業務【1】
C# WPFをメインで扱う会社。
本年6月より現職
以前は自社でも派遣でも作業するSE会社。
5年間で経験した言語は以下の通り;
C#: 2年5か月
Java:1年6か月
PHP:3か月
HTML・CSS:3か月
Excel VBA:1か月
ラダー :1か月(シーケンサ用の組込言語)
C、Mathematica、Matlab(大学時代に少しだけ)【2】
1.NUnit
2.MVVM
3.Prism
4.Code First(Entity Framework)
5.非同期処理
6.リフレクション
7.追記
■1.NUnit:
単体テスト自動化フレームワーク。使えれば絶対武器になると思っている。
なお、現在の職場は、以前の現場では「結合テスト」と呼んでいたテストを「単体テスト」としている。
私が以前学習(JSTQBテスト認定技術者試験)した、
単体で動くプログラムのテスト-たとえばデバッグによる単一クラスのテスト-
を、通常は「単体テスト」と呼ぶ。
■2.MVVM:
Microsoft の説明が分かりやすい。
https://docs.microsoft.com/ja-jp/archive/msdn-magazine/2009/february/patterns-wpf-apps-with-the-model-view-viewmodel-design-pattern
WPFで画面を閉じる処理が課題だったが、こちらを読みながら作成できた。
■3.Prism:
MVVM用のフレームワーク。職場のVSになぜかインストールできない。
■4.Entity Framework:
職場の環境上、Coreではなく、古いエディションのEntityFramework6を利用している。
DAO(そもそもこの言葉をなんとなくでしか理解できてないが)を作成する手間が減る。
■5.非同期処理:
マルチタスク。以前トライしたことはあるものの、効果的な実用には至らずだった。
当時は例外処理に泣いた。
今度は、その思想まで理解して実用化したい。
■6.リフレクション:
型推論というイメージ。
動的コードの利点は、コード量が減ること。
逆に欠点は、目でコードを見て理解することが難しくなること。
開発期間が厳しい、かつプロジェクトメンバーのレベルが高い場合、このリフレクションが選択肢としてあれば心強い。
■7.追記:
2.MVVM、6.リフレクションについては前職で経験済みだが、理解不足と感じているため。
LINQについても以前は比較的利用していたが、人間使わければ忘れてしまう。
今回、EntityFrameworkにて利用する機会があるのはありがたい。【3】
現在は研修という名目で、DB更新機能を備えたシステムを一人で作成中。
期間は1か月以上と潤沢。
目的は私の実力を把握するためとのことだが、
私自身は次の2つを目標としている;
・最低限の仕様書を残しながら、プロジェクトを計画から大幅な遅滞なく進めること。
・EntityFramework6、およびNUnit3を利用した開発により、開発時間の短縮、品質の向上を図ること。以上となります。
ここまで読んでいただき、ありがとうございました。
- 投稿日:2020-07-07T15:06:43+09:00
C# 入れ子のオブジェクトの内部構造を文字列に変換する方法 サンプルコード
このまま動作します。
コピぺ、編集して利用するためのコードの断片です。
自由に使って頂いて構いません。例えば、以下の構造のオブジェクトを、以下のような文字列に変換できます。
オブジェクトの入れ子に対応しています。
デバッカが使えない環境で、オブジェクトの内容をログに出力して、デバック等に使用できます。オブジェクトの構造.csusing System.Collections.Generic; class class_1 { public List<string> a; public int b = 0; public class_1 c; }変換した文字列▽(汎用.Form_Test+class_1)------------------------------- | ▽a(System.Collections.Generic.List`1[System.String])------------------------------- | | ▽_items(System.String[])------------------------------- | | | | | △_items(System.String[])------------------------------- | | | | _size(System.Int32) = 3 | | _version(System.Int32) = 0 | | _syncRoot = Nothing | | a[0](System.String) = "aaa" | | a[1](System.String) = "bbb" | | a[2](System.String) = "ccc" | | | △a(System.Collections.Generic.List`1[System.String])------------------------------- | | b(System.Int32) = 1 | ▽c(汎用.Form_Test+class_1)------------------------------- | | ▽a(System.Collections.Generic.List`1[System.String])------------------------------- | | | ▽_items(System.String[])------------------------------- | | | | | | | △_items(System.String[])------------------------------- | | | | | | _size(System.Int32) = 3 | | | _version(System.Int32) = 0 | | | _syncRoot = Nothing | | | a[0](System.String) = "ddd" | | | a[1](System.String) = "eee" | | | a[2](System.String) = "fff" | | | | | △a(System.Collections.Generic.List`1[System.String])------------------------------- | | | | b(System.Int32) = 2 | | c = Nothing | | | △c(汎用.Form_Test+class_1)------------------------------- | | △(汎用.Form_Test+class_1)-------------------------------以下コードでオブジェクトを文字列に変換できます。
呼び出しかた.css = ObjectToString(o);以下の共通処理を任意の場所に貼り付ければそのまま使用できます。
共通処理.csusing System.Text; using System.Reflection; /// <summary> /// 指定されたオブジェクトの内部構造の文字列表現を返却する。 /// </summary> /// <param name="targetObject">内部構造の文字列表現を取得する対象のオブジェクト</param> /// <returns>オブジェクトの内部構造の文字列表現</returns> public string ObjectToString(object targetObject) { var buf = new StringBuilder(); _ObjectToString("", targetObject, buf, ""); return buf.ToString(); } /// <summary> /// 指定されたオブジェクトの内部構造の文字列表現を返却する。 /// 内部呼び出し用なので、ObjectToString以外から直接呼び出さない。 /// </summary> /// <param name="targetFieldName">解析するオブジェクトのフィールド名</param> /// <param name="targetObject">内部構造の文字列表現を取得する対象のオブジェクト</param> /// <param name="buf">オブジェクトの内部構造の文字列表現の出力先バッファ</param> /// <param name="nestString">オブジェクトの入れ子構造を表現するための文字列</param> private void _ObjectToString(string targetFieldName, object targetObject, StringBuilder buf, string nestString) { try { if (nestString.Length >= 200) { throw new Exception("階層が深すぎるので解析を打ち切ります"); } if (targetObject == null) { buf.AppendLine(nestString + targetFieldName + " = Nothing"); } else if (targetObject.GetType() == typeof(byte) || targetObject.GetType() == typeof(short) || targetObject.GetType() == typeof(int) || targetObject.GetType() == typeof(long) || targetObject.GetType() == typeof(float) || targetObject.GetType() == typeof(double) || targetObject.GetType() == typeof(decimal) || targetObject.GetType() == typeof(char) || targetObject.GetType() == typeof(bool) || targetObject.GetType() == typeof(DateTime)) { buf.AppendLine(nestString + targetFieldName + "(" + targetObject.GetType().ToString() + ") = " + targetObject.ToString()); } else if (targetObject.GetType() == typeof(string)) { buf.AppendLine(nestString + targetFieldName + "(" + targetObject.GetType().ToString() + ") = \"" + targetObject.ToString().Replace("\r\n", "\r\n" + nestString + " ") + "\""); } else if (targetObject.GetType().ToString().IndexOf("System.Collections.Generic.List") == 0) { // 型:List(Of ...) への対応 // リストの内部情報はType.GetFieldsから解析する事ができないので、自前で解析する。 buf.AppendLine(nestString + "▽" + targetFieldName + "(" + targetObject.GetType().ToString() + ")-------------------------------"); foreach (var field in targetObject.GetType().GetFields(BindingFlags.FlattenHierarchy | BindingFlags.Public | BindingFlags.NonPublic | BindingFlags.Instance)) { _ObjectToString(field.Name, field.GetValue(targetObject), buf, nestString + "| "); } var count = (int)targetObject.GetType().GetProperty("Count").GetValue(targetObject, null); for (var index = 0; index <= count - 1; index++) { var param = new object[] { index }; var item = targetObject.GetType().GetProperty("Item").GetValue(targetObject, param); _ObjectToString(targetFieldName + "[" + index.ToString() + "]", item, buf, nestString + "| "); } buf.AppendLine(nestString + "|"); buf.AppendLine(nestString + "△" + targetFieldName + "(" + targetObject.GetType().ToString() + ")-------------------------------"); buf.AppendLine(nestString); } else { buf.AppendLine(nestString + "▽" + targetFieldName + "(" + targetObject.GetType().ToString() + ")-------------------------------"); foreach (var field in targetObject.GetType().GetFields(BindingFlags.FlattenHierarchy | BindingFlags.Public | BindingFlags.NonPublic | BindingFlags.Instance)) { _ObjectToString(field.Name, field.GetValue(targetObject), buf, nestString + "| "); } buf.AppendLine(nestString + "|"); buf.AppendLine(nestString + "△" + targetFieldName + "(" + targetObject.GetType().ToString() + ")-------------------------------"); buf.AppendLine(nestString); } } catch (Exception ex) { buf.AppendLine(nestString + "▽" + "(解析中に例外発生)-------------------------------"); buf.AppendLine(nestString + "| " + ex.ToString()); buf.AppendLine(nestString + "| " + ex.Message); buf.AppendLine(nestString + "| " + ex.StackTrace); buf.AppendLine(nestString + "|"); buf.AppendLine(nestString + "△" + "(解析中に例外発生)-------------------------------"); } }
- 投稿日:2020-07-07T12:27:49+09:00
C++/CLIでConsole.WriteLine()は表示されるのにprintf()やcoutは表示されない時の対処法
概要
VisualStudio2015にて、C++/CLIプロジェクトとC#プロジェクトを同居させたソリューションを作っていました。C++/CLIの方でprintf()やcoutでログ出力しようとしたのですが、VisualStudioでデバッグ実行すると表示されませんでした(プログラムはそのまま動いていました)。
Console.WriteLine()での出力は表示されており、なんでかなーと思っていたら解決したので、書いておきます。ググってもあまりヒットしなかったので・・・対処法
C#プロジェクトの、プロパティ⇒デバッグ⇒デバッガーを有効にするにある、「Visual Studio ホスティングプロセスを有効にする」チェックボックスのチェックを外すと表示されるようになりました。
その他
表示されていない時のことをもう少し詳しく書いておきます。
1つ目。表示されないと言いつつ、生成されたtest.exeを直接叩いた場合には、printf()やcoutで出力したログはコンソールに表示されていました。VisualStudioで動作させた場合には表示されていなかったという感じです。
2つ目。チェックボックスにチェックが入っている状態だと、実行時のコンソール上の表示が.exeではなく.EXEと大文字になっていました。
- 投稿日:2020-07-07T09:41:08+09:00
あの日見たScriptableObjectの型を僕達はまだ知らない
ScriptableObjectの読み込みを一元管理する
ScriptableObjectは便利ですよね。データをひとまとまりで取得できる優れものです。
今回はそれを読み込む流れを一元管理したいと思います。読み込みが管理されていないと、どうなる?
クラスとScriptableObjectが「多対多」の関係になってしまい、人間の脳に優しくありません。間に管理クラスを挟んで、「1対多」の関係を作る
管理クラス作成のために、ジェネリクスを使う
LoadData.csusing System.Collections; using System.Collections.Generic; using UnityEngine; public sealed class LoadData { private static LoadData instance = new LoadData(); public static LoadData Instance { get { return instance; } } // ScriptableObjectのみ受け付けるジェネリクスメソッド public T ScriptableLoader<T>(string path) where T : ScriptableObject { return Resources.Load<T>(path); } }こうすることで、ScriptableObjectが返されることを保証しつつ、様々な型のScriptableObjectを指定できます。
例
コード
今回使用したScrptableObjectです。
GameData.csusing System.Collections; using System.Collections.Generic; using UnityEngine; [CreateAssetMenu(menuName = "MyScript/Create GameData")] public class GameData : ScriptableObject { public int Data1; public string Data2; public float Data3; }これをもとに、ScriptableObjectを作成し、値を入れます。
それをStart()で表示してみます。
Game.csusing System.Collections; using System.Collections.Generic; using UnityEngine; public class Game : MonoBehaviour { // Start is called before the first frame update void Start() { Debug.Log(LoadData.Instance.ScriptableLoader<GameData>("Data/MyGameData").Data1); } }実行結果
おわりに
以上、「ScriptableObjectの型は使いたいクラスだけが知っていればいいよね。読み込むクラスはそんなの知らなくていいよ」という話でした。
- 投稿日:2020-07-07T09:21:12+09:00
ASP.NET Core MVC 3.1 入門 その3 「Controller」
はじめに
ASP.NET Core MVC 3.1 のコントローラーついて、自分が学んだことを備忘録として記載します。
このフレームワークに殆ど触れたことが無い方に少しでも参考になれば幸いです。
誤り等あれば、ご指摘頂けますと大変喜びます。前回の記事
ASP.NET Core MVC 3.1 入門 その2 「MVCプロジェクトの基本構造」
今回の流れ
- MVCパターンの説明
- コントローラーの説明
- コントローラーを作ってみる
- アクションメソッドの説明
- 動かしてみる
- 振り返り
今回のゴール
- MVCパターンにおけるコントローラーの役割を理解する
- コントローラーを追加できる
- アクションメソッドが何かを理解する
環境
- IDE
- Visual Studio 2019
- 言語
- C#
MVCパターン
コントローラーの説明に入る前に、
まずはMVCパターンについて確認しておきましょう。
全体像を把握したうえで、コントローラーについて具体的にみていきます。MVCパターンとは
画面表示と、ビジネスロジックの分離を実現するためのアーキテクチャ(設計手法)です。
要はHTMLの生成といった画面表示のための処理と
DBアクセスのようなデータの管理/操作をするための処理
これらを分離させるために先人が考え出したプログラムの作り方の一つです。MVCというのは構成要素の略称で、以下の単語の先頭文字をそれぞれ取り出したものです。
Model – View – Controller
実現できると、それぞれが分離しているので変更・修正があった場合、お互いに影響を受けづらく、Testability (テスト容易性) が高まるというメリットがあります。
画面に表示する項目の書式変更によってDBアクセス処理に影響が出たり、
DBの軽微な定義変更によって画面表示処理に影響が出るといった事態が発生しづらくなります。各要素の役割
要素 概要 Model データの管理/操作といったビジネスロジックを担当 View データ入力用のフォーム、処理結果の表示といったアプリケーションのフロントエンドを担当 Controller ModelとViewの橋渡しを担当 コントローラー
コントローラーとは
MVCの全体像を把握したうえで、 コントローラー(Controller)について具体的にみていきます。
MVCにおいて、 Model と View の橋渡しを担当する要素です。View から受け取った生データを Modelに引き渡し、
Model での処理結果を View に返すというのが主なお仕事です。
よくある会員登録処理で、 コントローラー のお仕事を確認してみましょう。
- 会員登録フォームに入力された情報を View から受け取る
→ View から生データを受け取る- 実際の会員登録処理(DBアクセス)を担当する Model に情報を引き渡す
→ 受け取ったデータを Model に引き渡す- 会員登録の処理結果(成功/既登録により失敗 etc)を受け取る
→ Model から処理結果を受け取る- ユーザーに会員登録の処理結果を通知するために、 View へ処理結果を返してあげる
→ 受け取った処理結果を View に返すコントローラーを追加する
では実際にコントローラーを作ってみましょう。
ASP.NET Core MVC 3.1 のプロジェクトを作成する
まずは「Web アプリケーション(モデル ビュー コントローラー)」テンプレートでプロジェクトを作成します。
ソリューションエクスプローラーを眺める
各フォルダ/ファイルの概要については前回の記事をご参照ください。
お作法として、 コントローラー は必ずControllersフォルダ配下に配置してください。コントローラー の追加
既にテンプレートに含まれている
HomeControllerが存在しますが、
新しいコントローラーを追加してみます。Controllersフォルダを右クリック > 追加 > コントローラー
と進んでください。
スキャフォールディング
以下のようなウィンドウが立ち上がると思います。
「スキャフォールディング」というのは、要は雛形を自動生成する機能のことを示します。
今回はコントローラーの雛形を自動生成してもらうために、この機能を使います。最初から全ての コントローラーの基底クラスとなる
Controllerクラスを継承してくれたり、読み取りや書き込み用のメソッドを用意してくれたりと、手間が省けるので、 コントローラーを追加する際には普通にクラスとして追加するのではなく、この手順でスキャフォールディング機能を利用することをおすすめします。今回は「MVC コントローラー - 空」を選択します。
よりリッチな雛形(CRUDに相当するメソッドが用意されている)もありますが、まずはまっさらなコントローラーを使っていきましょう。続いてファイル名ですが、
HelloControllerとします。
ここで重要なのは末尾をControllerとすることです。
末尾がControllerでないと、そのままではコントローラーとして検出されません。
※末尾がControllerでない場合は、Controller属性を付与する必要があります。
クラス名からControllerを取り除いた部分がコントローラー名とみなされます。コントローラークラス
以上でコントローラーは追加できたと思います。
では、追加されたコントローラーのコードを見ていきましょう。HelloController
まず注目して頂きたいのは、
Controllerクラスを継承している点です。
コントローラーは通常Microsoft.AspNetCore.Mvc.Controllerクラスを継承します。
※前身のASP.NET MVC では必須でした。public class HelloController : Controller { public IActionResult Index() { return View(); } }アクションメソッド
次に
Indexというメソッドをみていきます。
今回は「MVC コントローラー - 空」を使用しましたが、このIndexメソッドは最初から定義されています。このメソッドは「アクションメソッド」と呼ばれるものです。
public IActionResult Index() { return View(); }アクションメソッドとは
クライアントからの1リクエスト(ex 登録ボタンの押下)に対して、具体的な処理内容を定義するメソッドです。
上記のIndexメソッドが正にアクションメソッドとなります。
Controllerクラスとは、関連性を持った複数のアクションメソッドによって構成されます。
逆に言えば、一つ以上のアクションメソッドを束ねたクラスがコントローラーとなります。アクションメソッドの規約
メソッドをアクションメソッドとして定義するためには、以下に代表される規約を全て守る必要があります。
- コントローラークラス内に定義する
publicstaticでない- 戻り値が
IActionResultオブジェクト(後述)上記の
Indexアクションメソッドは上記の規約を全て守っているので、アクションメソッドということになります。アクションメソッドの戻り値
繰り返しになりますが、アクションメソッドとはクライアントからの1リクエスト(ex 登録ボタンの押下)に対して、具体的な処理内容を定義するメソッドです。
従って、様々な処理をおこなうことが予想されます。
当然、様々な処理の結果、結果も様々となります。
- 文字列
- ファイルのデータ
- 別のページへの遷移(リダイレクト)
- 要求されたページが存在しない という結果
上記のような様々な結果を表現するために 「IActionResult」を実装した様々なクラス(
IActionResultオブジェクト)が用意されています。この
IActionResultインターフェースはアクションメソッド内での処理に代わり、実際のHTTPレスポンスを生成する機能を提供しています。
我々が、HTTPレスポンスを直接的に意識することなく、開発ができるのはこの仕組みのおかげというわけです。代表的な
IActionResultオブジェクトを以下で紹介します。主なIActionResultオブジェクト
型 概要 ContentResult テキストデータを返す JsonResult JSON 形式のデータを返す ViewResult テンプレート(ビュー)に基づいてページを返す FileContentResult 指定されたバイト配列をファイルとして返す RedirectResult 指定のアドレスに移動(リダイレクト)する RedirectToActionResult 指定のアクションに移動(リダイレクト)する NotFoundResult ステータスコード 404(NotFound)エラー を返す EmptyResult なにもしない 利用頻度の高い
IActionResultオブジェクトについては、対応するファクトリメソッドがControllerクラスに用意されています。
対応するファクトリメソッドが存在しないものについては、自分でnewする必要があります。代表的なものを以下に紹介します。
主なIActionResultオブジェクトのファクトリメソッド
ファクトリメソッド IActionResultオブジェクト 備考 Content ContentResult Json JsonResult View ViewResult デフォルトでは「/Views/コントローラー名/アクション名.cshtml」を読み込む File FileContentResult Redirect RedirectResult リダイレクト先をURLで指定する RedirectToAction RedirectToActionResult リダイレクト先をアクション名(+コントローラー名)で指定する NotFound NotFoundResult ステータスコードを返却する場合は、StatusCodeResultクラスを利用することもできる(対応するファクトリメソッドがないステータスコードも存在する) 動作確認
アクションメソッドを修正する
自動生成された
Indexアクションメソッドを以下のように修正します。
現時点では、Viewを追加していないので、Viewテンプレートを経由せずにテキストデータを返すContentResultを戻り値として返してみます。
Contentメソッドは、上述した通りContentResultのファクトリメソッドです。public class HelloController : Controller { public IActionResult Index() { return Content("Hello World!"); //修正箇所 } }実行
修正したら、F5で実行してみましょう。
テンプレートで用意されているトップページが表示されると思います。ここでアドレスバーから手入力でURLを変更します。
変更前は以下のようになっているかと思います。
XXXXXの部分はポート番号を示しています。
環境とタイミング次第で異なりますので、ご自身の画面に表示されているものから変更する必要はありません。https://localhost:XXXXX/以下のようにアドレスバーから手入力でURLを変更してください。
末尾にHello/Indexを追加します。https://localhost:XXXXX/Hello/Index変更後、Enterを押下すると、以下のように「Hello World!」表示されると思います。
以上で動作確認は終了です。
Hello/Indexとすると、なぜHelloControllerのIndexアクションメソッドが呼び出せるのかについては、次回以降の記事にします。アクションメソッドを振り返る
ここまでのおさらいも兼ねて、以下のメソッドを言葉にすると
Contentメソッドを呼び出し、テキストデータを返すためのIActionResultオブジェクト(ContentResult)を生成し、戻り値として返すIndexアクションメソッドということになります。
public class HelloController : Controller { public IActionResult Index() { return Content("Hello World!"); } }
- 投稿日:2020-07-07T05:52:02+09:00
C系プログラマが始めるPython
Pythonを勉強することになったきっかけ
前々から話題になっており触りたいと思っていたまま、時が過ぎてました...
つい最近、Python触れる子が周りにいたり、企業の人にも他の言語にも触れてみたらとアドバイスをもらったりしたので勉強するモチベが高くなったって感じです。余談
この画像はちょまどさんが描かれたものです。Pythonちゃん可愛いですよね!
この記事を読む前に
投稿主はC/C++、Java、C#をすでに触っているため、そういったC系の言語と比較した視点で記事を書いていくので予めご了承ください。
お〇ぱいそんを触りたい
ド下ネタな見出しですみません。ただ言いたいだけです。
ここから具体的なPythonの基本文法についてまとめてます。参考記事の内容を自分なりに省略しているので詳細は参考記事をご覧ください。セミコロンが要らない
タイトルの通り過ぎてこれ以上書くことがないですが、文末にセミコロン付けなくていいらしので1タイプ手間が減りますね!!
個人的には普段C系の言語を触っているので違和感しか感じませんが笑型の指定が要らない
C/C++などでは変数を宣言する際に型を明示しなければなりません。
C++int num = 100;これがPythonだとこうなります。
Pythonnum = 100C++やC#でいうautoやvar(型推論)的なことをPythonはデフォルトでやってるみたいですね。(動的型付け)
ただ、明示した方が良いこともあるので一長一短ですね。
ちなみに関数の引数や戻り値っも同様に型指定がありません。中括弧が要らない(ifやforに括弧が要らない)
ないないだらけですね笑
Pythonnum = 77 if num % 2 == 0: print("偶数です") else: print("奇数です")中括弧がない代わりに:(コロン)で条件と処理を分けてるみたいですね。インデントをすることで階層を判断するみたいです
またifやfor、whileなどの条件に括弧をつけないそうです。
これなら中括弧の位置で戦争することが無いので平和だぁ...switch~case、do~whileが無い
タイトルの通りありません。do~whileについてはほぼ使わないんですけどね。
switchの代わりにif~elif~elseで代用するみたいです。else ifではなくてelifみたいなので注意。(なぜ省略したんだろ...)リストに型の制約がない
Cでいう配列なのですが、配列と違いPythonのリストは型の制約がないので数値型や文字列が混在していても大丈夫なようです。
Pythonのforは拡張for(C#でいうforeach)
PythonarrayList = ["cat",100,25.25,"Python"] #リスト内をぶん回します for array in arrayList: print(array) #indexで指定してforを回したい場合 for index in range(0,10): print(index)上のコードのコメントの通りです。rangeを使うことで0から9の配列を用意しています。
ちなみにコメントは// の代わりが # で /*~*/ の代わりが ''' ~ '''です。演算子系
- インクリメント・デクリメント演算子が存在しない(+=や-=では使える)
- //(小数点以下を切り捨て、整数値を低い方に合わせる)や**(べき乗)がある
- 論理演算の&& や || 、! が and、 or、 notという予約語として扱われる。
関数
Pythonの関数は関数オブジェクトとして扱われています。
Pythondef Add(x,y): return x + y前述したとおり型指定が要らないのでこういう風に描けます。手前にdefを付けたら関数です。物凄くシンプルで好き。
しかもこれの凄いところが型指定が無いのでこんなこともできます。Pythonprint(Add(2,5)) print(Add(1.6,4.8)) print(Add("Hello","World"))C++やC#でいうテンプレート関数として扱えます。(<>で型指定しないのが便利すぎる)
ただ関数のオーバーロードが出来ないそうです。似たようなことをしようと思ったらデフォルト引数を用いればできるみたい。pass文
pass文っていう処理書かなくてエラー出ずに通るっていう文があります。
Pythondef AddPosition(): #TODO:後で実装 passこんな感じで後で実装したいときとかにpass文を使うそうです。なるほどーって感じです。
その他
- import が Cでいう #include
- 連想配列(dict)、タプルが標準で使える
- const や readonly、finalなどの定数のキーワードが無い。(慣習で大文字の変数は定数と扱っているみたい)
- public や private などのアクセス指定子がない。(慣習でprivateなものには _を 1つ付けるとかなんとか)
- virtualやoverrideがない。(継承して同名のメソッドを定義すれば可能)
- 多重継承が可能。(その代わりjavaでいうインターフェースがない)
まとめ
ぱーっと基本的な文法についてはこんなものかなと思います。C++やC#でもあるような機能は省いています。詳細は参考記事をご覧ください。
軽く勉強してみて感じたのは可読性の高いコードを書きやすいなと思いました。
今後はPythonを用いて自動化やAIについての勉強もできたらなと思います。またPythonについてご意見やアドバイスがありましたら気軽にコメントくださると嬉しいです!
参考記事
C言語系プログラマのためのPython入門
https://qiita.com/shiracamus/items/fd35c685e9679323471fC#エンジニアが初めてpythonを学習して感じた違い
https://qiita.com/CEML/items/29944cbeb8e38171a630C#からPythonに乗りかえるための両者の比較
https://qiita.com/kent-u/items/e6b210d38ca39b4cd107C#erがPythonを勉強したので、違いについて比較しながら述べてみる その1 構文
https://hiroronn.hatenablog.jp/entry/20170717/1500281854Python入門
https://python.keicode.com/
- 投稿日:2020-07-07T02:20:30+09:00
C#とfortran(DLL)を連携させる
C#からFortran(DLL)を呼び出すテスト用PGを作成したので、健忘録として残しておきます。Intel Fortranでコンパイルする場合、エントリポイントの指定の仕方が下記と異なるので注意してください。
C#⇔Fortranを行う際、文字列は半角空白埋め、構造体をそれぞれ定義して値の受け渡しを行っています。環境
CPU:CoreTM i5-6400(2.70GHz)
物理メモリー:8GB
Windows 10 Pro(64bit),MS Office 2016(32bit)コンパイラ
- gfortran (gcc version 8.1.0)
- Visual Studio Express 2015
Fortran(fortranLib.f03)のコード
Fortran側で作成した関数は以下の通り
関数名 説明 TranslateViaTypes 構造体を作成し、module内のtestDataという構造体に値を格納する TranslateViaInputFile 引数として入力ファイルの絶対パスを渡すと、対象ファイルの内容を読み込みmodule内の構造体に保存する GetData 入力引数として指定した行位置での構造体を取得し、C#側に出力引数として渡す FortranLib.f03module FortranLib implicit none !構造体 type, bind(c) :: PointType integer :: Id real(8) :: Coords(3) end type !ここに書いておけばPublic/Privateに使えるので、固定値や関数とか書いておく !省略だとpublic !USE側でreadオンリーで使用等も指定可能 integer, parameter :: STRC_SIZE = 10 type(PointType),target,save:: testdata(STRC_SIZE) end module FortranLib !テスト1 C#側から渡された入力ファイルパスを受け取り、対象ファイルの内容を受け取る !入力引数:対象ファイルの絶対パス function TranslateViaInputFile(inputPath) use FortranLib implicit none !GCC$ ATTRIBUTES DLLEXPORT :: TranslateViaInputFile character(256), intent(in) :: inputPath integer :: ios integer :: i logical :: TranslateViaInputFile type(PointType), pointer :: point TranslateViaInputFile = .false. open(unit=10,file = Trim(inputPath), action='read', form='formatted', status='old') do i=1, STRC_SIZE, 1 point => testdata(i) read(10,*, iostat=ios) point%ID,point%Coords(:) if (ios < 0) exit end do close(10) TranslateViaInputFile = .true. end function TranslateViaInputFile !テスト2 ポインターを使い、構造体に値を格納 Function TranslateViaTypes() use FortranLib implicit none !GCC$ ATTRIBUTES DLLEXPORT :: TranslateViaTypes logical ::TranslateViaTypes integer :: i type(PointType), pointer :: point !ポインタはmoduleに書いた通り、IDと数字三つでワンセット TranslateViaTypes = .false. do i = 1, STRC_SIZE point => testdata(i) !参照 point%ID = i point%Coords(1) = i point%Coords(2) = i + 1 point%Coords(3) = i + 2 end do TranslateViaTypes = .true.!成功したらTrueを返す end Function TranslateViaTypes !テスト3 ポインターを使い、指定された行の構造体を返す !入力引数 n:指定された行数 !出力引数 outPutData:n行目の構造体 !戻り値 bool 成功したか否か Function GetData(n,outPutData) use FortranLib implicit none !GCC$ ATTRIBUTES DLLEXPORT :: GetData integer, intent(in) :: n logical ::GetData type(PointType), intent(inout),target:: outPutData(1) type(PointType), pointer :: point !初期化 GetData = .false. !同じ値を参照するようにする point => outPutData(1) !inputdataと値は独立しているか確認 !point%ID = inputData(n)%ID !point%Coords(:) = inputData(n)%Coords(:)+1 point%ID = testdata(n)%ID point%Coords(:) = testdata(n)%Coords(:) GetData = .true.!成功したらTrueを返す end Functiongfortranでのコンパイルオプション
staticを指定しないと、開発環境がないと動かない
-static-libgfortran -static-libgcc だけでは他環境で動かなかったgfortran -c -fno-underscoring -Wall -Wc-binding-type -Wuninitialized FortranLib.f03 -o FortranLib.o gfortran -shared -mrtd -fno-underscoring -"Wl,--kill-at" -fdump-core -fbacktrace -w -Wall -static -Wc-binding-type FortranLib.o -o FortranLib.dllFortranでreadするファイルの内容
test.txtid,{x,y,z} 500,11,12,13 501,21,22,23 502,31,32,33 503,41,42,43 504,51,52,53 505,61,62,63 506,71,72,73 507,81,82,83 508,91,92,93 509,101,102,103C# (FortranLib.cs)
FortranLib.csusing System; using System.Collections.Generic; using System.IO; using System.Linq; using System.Runtime.InteropServices; using System.Text; using System.Threading.Tasks; namespace test { [StructLayout(LayoutKind.Sequential, Pack = 8)] public struct Point { public int Id; [MarshalAs(UnmanagedType.ByValArray, SizeConst = 3)] public double[] Coords; public Point(int id, double x, double y, double z) { this.Id = id; this.Coords = new double[3] { x, y, z }; } } public static class FortranLib { private const string _dllName = @".\FortranLib.dll"; public const int PathLength = 256; //moduleの内部手続きとして定義した場合 //entrypointでの名前がモジュールの内部手続き扱いになっている //[DllImport(_dllName, CharSet = CharSet.Ansi, CallingConvention = CallingConvention.Cdecl, EntryPoint = "__fortranlib_MOD_translateviaarrays")] //public static extern void TranslateViaArrays([In] double[] delta, ref int n, [In, Out] double[,] coords); //module共有(module test)で完全別手続きにした [DllImport(_dllName, CharSet = CharSet.Ansi, CallingConvention = CallingConvention.Cdecl, EntryPoint = "translateviaarrays")] public static extern void TranslateViaArrays([In] double[] delta, ref int n, [In, Out] double[,] coords); [DllImport(_dllName, CharSet = CharSet.Ansi, CallingConvention = CallingConvention.Cdecl, EntryPoint = "translateviainputfile")] public static extern bool TranslateViaInputfile([In] char[] inputPath); [DllImport(_dllName, CharSet = CharSet.Ansi, CallingConvention = CallingConvention.Cdecl, EntryPoint = "translateviatypes")] public static extern Boolean TranslateViaTypes(); [DllImport(_dllName, CharSet = CharSet.Ansi, CallingConvention = CallingConvention.Cdecl, EntryPoint = "getdata")] public static extern Boolean GetData(ref int n, [In, Out] Point[] outputData); /// <summary> /// Fortran用半角空白文字埋め /// </summary> /// <param name="source">文字列</param> /// <param name="length">文字列の長さ</param> /// <returns>指定した長さに合わせて半角空白埋めをした文字列</returns> public static char[] ToCharacterArrayFortran(this string source, int length) { var chars = new char[length]; int sourceLength = source.Length; for (int i = 0; i < length; i++) { if (i < sourceLength) chars[i] = source[i]; else chars[i] = ' '; } return chars; } } }C#で作成した画面用PG
Formに配置するコントロール 説明 rowData 行数入力用のNumericUpDown OpenData 配列(構造体)作成ボタン readData ファイル読み込みボタン openDataButton データ表示ボタン Form.csusing System; using System.Collections.Generic; using System.ComponentModel; using System.Data; using System.Drawing; using System.IO; using System.Linq; using System.Text; using System.Threading.Tasks; using System.Windows.Forms; namespace test { public partial class Main : Form { public Main() { InitializeComponent(); } /// <summary> /// データ表示ボタン押下時のイベント /// </summary> /// <param name="sender">イベントのオブジェクト</param> /// <param name="e">イベントのプロパティ</param> private void OpenData_Click(object sender, EventArgs e) { //FortranCall テスト1 C#から渡された配列内の値をFortran内で変更して戻す RunTranslateViaTypes(); } /// <summary> /// ファイル読み込みボタン /// </summary> /// <param name="sender">イベントのオブジェクト</param> /// <param name="e">イベントのプロパティ</param> private void readData_Click(object sender, EventArgs e) { RunTranslateViaInputFile(); } /// <summary> /// 画面読み込み /// </summary> /// <param name="sender">イベントのオブジェクト</param> /// <param name="e">イベントのプロパティ</param> private void Main_Load(object sender, EventArgs e) { //画面の大きさの変更はなし this.FormBorderStyle = FormBorderStyle.FixedSingle; //フォームの最大化ボタンの表示、非表示を切り替える MaximizeBox = false; //フォームの最小化ボタンの表示、非表示を切り替える MinimizeBox = false; } /// <summary> /// データ表示ボタン /// </summary> /// <param name="sender">イベントのオブジェクト</param> /// <param name="e">イベントのプロパティ</param> private void openDataButton_Click(object sender, EventArgs e) { int rowValue = (int)rowData.Value; GetData(rowValue); } /// <summary> /// Fortran内関数呼び出しテスト用 /// </summary> private static void RunTranslateViaArrays() { var delta = new[] { 1.0, 2.0, 3.0 }; int n = 10; //fortran側へ渡す&変更される配列 var coords = new double[n, 3]; for (int i = 0; i < n; i++) { coords[i, 0] = i; coords[i, 1] = i + 1; coords[i, 2] = -i; } FortranLib.TranslateViaArrays(delta, ref n, coords); //Call後coords内の値が変更されていることがわかる //コンソールに出力し確認 for (int i = 0; i < n; i++) Console.WriteLine("Point {0}: ({1}, {2}, {3})", i + 1, coords[i, 0], coords[i, 1], coords[i, 2]); } /// <summary> /// Fortran内関数呼び出しテスト2 /// </summary> private static void RunTranslateViaInputFile() { string inputPath = Path.Combine(AppDomain.CurrentDomain.BaseDirectory, "test.txt"); // 一端文字列をFortran用に渡す(未使用部分半角空白埋め) char[] inputPathChars = inputPath.ToCharacterArrayFortran(FortranLib.PathLength); long returnValue=0; //文字列をFortranへ渡す bool resultValue = FortranLib.TranslateViaInputfile(inputPathChars); if (resultValue) { MessageBox.Show("ファイルを読み込みました", "確認", MessageBoxButtons.OK, MessageBoxIcon.Information); } else { MessageBox.Show("読み込みが失敗しました", "エラー", MessageBoxButtons.OK, MessageBoxIcon.Warning); } } /// <summary> /// 指定した格納領域にFortranで格納した構造体を受け取る /// </summary> private static void RunTranslateViaTypes() { int n = 10;//行数 //Fortran内関数を呼ぶ 第二引数で指定したメモリ領域に値を格納 bool resultValue = FortranLib.TranslateViaTypes(); //上記実行後、pointにはFortran側で格納した値が入っている if (resultValue) { MessageBox.Show("配列を作成しました", "確認", MessageBoxButtons.OK, MessageBoxIcon.Information); } else { MessageBox.Show("配列作成が失敗しました", "エラー", MessageBoxButtons.OK, MessageBoxIcon.Warning); } } /// <summary> /// 指定された行数に該当するデータを取得する /// </summary> /// <param name="rowValue">取得する行数</param> private static void GetData(int rowData) { Point[] outputData = new Point[1]; bool resultValue = FortranLib.GetData(ref rowData, outputData); if (resultValue) { MessageBox.Show("配列を取得しました" + Environment.NewLine + $@"ID:{outputData[0].Id} x:{outputData[0].Coords[0]} y:{outputData[0].Coords[1]} z:{outputData[0].Coords[2]}", "確認", MessageBoxButtons.OK, MessageBoxIcon.Information); } else { MessageBox.Show("配列取得が失敗しました", "エラー", MessageBoxButtons.OK, MessageBoxIcon.Warning); } } } }参考
- 投稿日:2020-07-07T02:20:30+09:00
C#とFortran(DLL)を連携させる
C#からFortran(DLL)を呼び出すテスト用PGを作成したので、健忘録として残しておきます。Intel Fortranでコンパイルする場合、エントリポイントの指定の仕方が下記と異なるので注意してください。
C#⇔Fortranを行う際、文字列は半角空白埋め、構造体をそれぞれ定義して値の受け渡しを行っています。環境
CPU:CoreTM i5-6400(2.70GHz)
物理メモリー:8GB
Windows 10 Pro(64bit),MS Office 2016(32bit)コンパイラ
- gfortran (gcc version 8.1.0)
- Visual Studio Express 2015
Fortran(fortranLib.f03)のコード
Fortran側で作成した関数は以下の通り
関数名 説明 TranslateViaTypes 構造体を作成し、module内のtestDataという構造体に値を格納する TranslateViaInputFile 引数として入力ファイルの絶対パスを渡すと、対象ファイルの内容を読み込みmodule内の構造体に保存する GetData 入力引数として指定した行位置での構造体を取得し、C#側に出力引数として渡す FortranLib.f03module FortranLib implicit none !構造体 type, bind(c) :: PointType integer :: Id real(8) :: Coords(3) end type !ここに書いておけばPublic/Privateに使えるので、固定値や関数とか書いておく !省略だとpublic !USE側でreadオンリーで使用等も指定可能 integer, parameter :: STRC_SIZE = 10 type(PointType),target,save:: testdata(STRC_SIZE) end module FortranLib !テスト1 C#側から渡された入力ファイルパスを受け取り、対象ファイルの内容を受け取る !入力引数:対象ファイルの絶対パス function TranslateViaInputFile(inputPath) use FortranLib implicit none !GCC$ ATTRIBUTES DLLEXPORT :: TranslateViaInputFile character(256), intent(in) :: inputPath integer :: ios integer :: i logical :: TranslateViaInputFile type(PointType), pointer :: point TranslateViaInputFile = .false. open(unit=10,file = Trim(inputPath), action='read', form='formatted', status='old') do i=1, STRC_SIZE, 1 point => testdata(i) read(10,*, iostat=ios) point%ID,point%Coords(:) if (ios < 0) exit end do close(10) TranslateViaInputFile = .true. end function TranslateViaInputFile !テスト2 ポインターを使い、構造体に値を格納 Function TranslateViaTypes() use FortranLib implicit none !GCC$ ATTRIBUTES DLLEXPORT :: TranslateViaTypes logical ::TranslateViaTypes integer :: i type(PointType), pointer :: point !ポインタはmoduleに書いた通り、IDと数字三つでワンセット TranslateViaTypes = .false. do i = 1, STRC_SIZE point => testdata(i) !参照 point%ID = i point%Coords(1) = i point%Coords(2) = i + 1 point%Coords(3) = i + 2 end do TranslateViaTypes = .true.!成功したらTrueを返す end Function TranslateViaTypes !テスト3 ポインターを使い、指定された行の構造体を返す !入力引数 n:指定された行数 !出力引数 outPutData:n行目の構造体 !戻り値 bool 成功したか否か Function GetData(n,outPutData) use FortranLib implicit none !GCC$ ATTRIBUTES DLLEXPORT :: GetData integer, intent(in) :: n logical ::GetData type(PointType), intent(inout),target:: outPutData(1) type(PointType), pointer :: point !初期化 GetData = .false. !同じ値を参照するようにする point => outPutData(1) !inputdataと値は独立しているか確認 !point%ID = inputData(n)%ID !point%Coords(:) = inputData(n)%Coords(:)+1 point%ID = testdata(n)%ID point%Coords(:) = testdata(n)%Coords(:) GetData = .true.!成功したらTrueを返す end Functiongfortranでのコンパイルオプション
static未指定では、gfortranの環境がないと動かない
-static-libgfortran -static-libgcc だけでは他環境で動かなかった
gfortran -c -fno-underscoring -Wall -Wc-binding-type -Wuninitialized FortranLib.f03 -o FortranLib.o gfortran -shared -mrtd -fno-underscoring -"Wl,--kill-at" -fdump-core -fbacktrace -w -Wall -static -Wc-binding-type FortranLib.o -o FortranLib.dllFortranでreadするファイルの内容
※実際にはヘッダーはなし
test.txtid,{x,y,z} 500,11,12,13 501,21,22,23 502,31,32,33 503,41,42,43 504,51,52,53 505,61,62,63 506,71,72,73 507,81,82,83 508,91,92,93 509,101,102,103C# (FortranLib.cs)
FortranLib.csusing System; using System.Collections.Generic; using System.IO; using System.Linq; using System.Runtime.InteropServices; using System.Text; using System.Threading.Tasks; namespace test { [StructLayout(LayoutKind.Sequential, Pack = 8)] public struct Point { public int Id; [MarshalAs(UnmanagedType.ByValArray, SizeConst = 3)] public double[] Coords; public Point(int id, double x, double y, double z) { this.Id = id; this.Coords = new double[3] { x, y, z }; } } public static class FortranLib { private const string _dllName = @".\FortranLib.dll"; public const int PathLength = 256; //moduleの内部手続きとして定義した場合 //entrypointでの名前がモジュールの内部手続き扱いになっている //[DllImport(_dllName, CharSet = CharSet.Ansi, CallingConvention = CallingConvention.Cdecl, EntryPoint = "__fortranlib_MOD_translateviaarrays")] //public static extern void TranslateViaArrays([In] double[] delta, ref int n, [In, Out] double[,] coords); //module共有(module test)で完全別手続きにした [DllImport(_dllName, CharSet = CharSet.Ansi, CallingConvention = CallingConvention.Cdecl, EntryPoint = "translateviaarrays")] public static extern void TranslateViaArrays([In] double[] delta, ref int n, [In, Out] double[,] coords); [DllImport(_dllName, CharSet = CharSet.Ansi, CallingConvention = CallingConvention.Cdecl, EntryPoint = "translateviainputfile")] public static extern bool TranslateViaInputfile([In] char[] inputPath); [DllImport(_dllName, CharSet = CharSet.Ansi, CallingConvention = CallingConvention.Cdecl, EntryPoint = "translateviatypes")] public static extern Boolean TranslateViaTypes(); [DllImport(_dllName, CharSet = CharSet.Ansi, CallingConvention = CallingConvention.Cdecl, EntryPoint = "getdata")] public static extern Boolean GetData(ref int n, [In, Out] Point[] outputData); /// <summary> /// Fortran用半角空白文字埋め /// </summary> /// <param name="source">文字列</param> /// <param name="length">文字列の長さ</param> /// <returns>指定した長さに合わせて半角空白埋めをした文字列</returns> public static char[] ToCharacterArrayFortran(this string source, int length) { var chars = new char[length]; int sourceLength = source.Length; for (int i = 0; i < length; i++) { if (i < sourceLength) chars[i] = source[i]; else chars[i] = ' '; } return chars; } } }C#で作成した画面用PG
Formに配置するコントロール 説明 rowData 行数入力用のNumericUpDown OpenData 配列(構造体)作成ボタン readData ファイル読み込みボタン openDataButton データ表示ボタン Form.csusing System; using System.Collections.Generic; using System.ComponentModel; using System.Data; using System.Drawing; using System.IO; using System.Linq; using System.Text; using System.Threading.Tasks; using System.Windows.Forms; namespace test { public partial class Main : Form { public Main() { InitializeComponent(); } /// <summary> /// データ表示ボタン押下時のイベント /// </summary> /// <param name="sender">イベントのオブジェクト</param> /// <param name="e">イベントのプロパティ</param> private void OpenData_Click(object sender, EventArgs e) { //FortranCall テスト1 C#から渡された配列内の値をFortran内で変更して戻す RunTranslateViaTypes(); } /// <summary> /// ファイル読み込みボタン /// </summary> /// <param name="sender">イベントのオブジェクト</param> /// <param name="e">イベントのプロパティ</param> private void readData_Click(object sender, EventArgs e) { RunTranslateViaInputFile(); } /// <summary> /// 画面読み込み /// </summary> /// <param name="sender">イベントのオブジェクト</param> /// <param name="e">イベントのプロパティ</param> private void Main_Load(object sender, EventArgs e) { //画面の大きさの変更はなし this.FormBorderStyle = FormBorderStyle.FixedSingle; //フォームの最大化ボタンの表示、非表示を切り替える MaximizeBox = false; //フォームの最小化ボタンの表示、非表示を切り替える MinimizeBox = false; } /// <summary> /// データ表示ボタン /// </summary> /// <param name="sender">イベントのオブジェクト</param> /// <param name="e">イベントのプロパティ</param> private void openDataButton_Click(object sender, EventArgs e) { int rowValue = (int)rowData.Value; GetData(rowValue); } /// <summary> /// Fortran内関数呼び出しテスト用 /// </summary> private static void RunTranslateViaArrays() { var delta = new[] { 1.0, 2.0, 3.0 }; int n = 10; //fortran側へ渡す&変更される配列 var coords = new double[n, 3]; for (int i = 0; i < n; i++) { coords[i, 0] = i; coords[i, 1] = i + 1; coords[i, 2] = -i; } FortranLib.TranslateViaArrays(delta, ref n, coords); //Call後coords内の値が変更されていることがわかる //コンソールに出力し確認 for (int i = 0; i < n; i++) Console.WriteLine("Point {0}: ({1}, {2}, {3})", i + 1, coords[i, 0], coords[i, 1], coords[i, 2]); } /// <summary> /// Fortran内関数呼び出しテスト2 /// </summary> private static void RunTranslateViaInputFile() { string inputPath = Path.Combine(AppDomain.CurrentDomain.BaseDirectory, "test.txt"); // 一端文字列をFortran用に渡す(未使用部分半角空白埋め) char[] inputPathChars = inputPath.ToCharacterArrayFortran(FortranLib.PathLength); long returnValue=0; //文字列をFortranへ渡す bool resultValue = FortranLib.TranslateViaInputfile(inputPathChars); if (resultValue) { MessageBox.Show("ファイルを読み込みました", "確認", MessageBoxButtons.OK, MessageBoxIcon.Information); } else { MessageBox.Show("読み込みが失敗しました", "エラー", MessageBoxButtons.OK, MessageBoxIcon.Warning); } } /// <summary> /// 指定した格納領域にFortranで格納した構造体を受け取る /// </summary> private static void RunTranslateViaTypes() { int n = 10;//行数 //Fortran内関数を呼ぶ 第二引数で指定したメモリ領域に値を格納 bool resultValue = FortranLib.TranslateViaTypes(); //上記実行後、pointにはFortran側で格納した値が入っている if (resultValue) { MessageBox.Show("配列を作成しました", "確認", MessageBoxButtons.OK, MessageBoxIcon.Information); } else { MessageBox.Show("配列作成が失敗しました", "エラー", MessageBoxButtons.OK, MessageBoxIcon.Warning); } } /// <summary> /// 指定された行数に該当するデータを取得する /// </summary> /// <param name="rowValue">取得する行数</param> private static void GetData(int rowData) { Point[] outputData = new Point[1]; bool resultValue = FortranLib.GetData(ref rowData, outputData); if (resultValue) { MessageBox.Show("配列を取得しました" + Environment.NewLine + $@"ID:{outputData[0].Id} x:{outputData[0].Coords[0]} y:{outputData[0].Coords[1]} z:{outputData[0].Coords[2]}", "確認", MessageBoxButtons.OK, MessageBoxIcon.Information); } else { MessageBox.Show("配列取得が失敗しました", "エラー", MessageBoxButtons.OK, MessageBoxIcon.Warning); } } } }参考











