- 投稿日:2020-07-10T23:33:26+09:00
DI って何でするのかわからない人向けに頑張って説明してみる「本来の意味」
DI (依存性注入) って何のためにするのかわからない人向けに頑張って説明してみる を、単体テストにフォーカスしたら単体テストが本来の目的では無い的なコメントが散見されました。
確かに DI コンテナは元々は SOLID 原則の D に該当する依存性逆転の原則 (Dependency inversion principle) の「具体ではなく、抽象に依存しなければならない」に大きく関係するものになります。
Wikipedia から引用します。
ソフトウエアモジュールを疎結合に保つための特定の形式を指す用語。
この原則に従うとソフトウェアの振る舞いを定義する上位レベルのモジュールから
下位レベルモジュールへの従来の依存関係は逆転し、結果として下位レベルモジュールの
実装の詳細から上位レベルモジュールを独立に保つことができるようになる。例えば以下のように上位レベルのモジュール(Presentation)が下位レベルのモジュール(Application)にがっちり依存してるとします。
class SomePresentation { public void Foo() { var c = new SomeApplication(); c.Bar(); } } class SomeApplication { public void Bar() { } }クラス図にするとこんな感じですね。 (メソッドは省略)
こんな風に上位のクラス(Presentation)が、より下位のクラス (Application) に依存していると上位のクラスの再利用が妨げられます。SomeApplication をがっちりつかんでて使いまわししづらいです。芋づる式に上位のクラスを使おうと思うと下位のクラスにがついてきます。
依存性の逆転
実装に直接依存するのではなく、間に抽象を挟むことで実装に依存しなくなります。
ISomeApplication は、SomePresentation の都合によって定義されるもので SomePresentation の一部だと考えると SomeApplication から Presentation 側に矢印が向いてる感じになってるように見えますね。矢印の向きが反対になりました。
組み立てないと…
こういう風に定義すると、やっぱりクラスは組み立てないといけません。そのために、こんな感じのクラスを組み立てる役割を負う人が必要になります。
こういう風にしておかないと SomePresentation を使う人全員がクラスを正しく組み立てないといけないという責任を負うことになります。それはつらいよね。class PresentationFactory { public SomePresentation CreateSomePresentation() => new SomePresentation( new SomeApplication(new SomeRepository()) // 今回は未登場だけど Application は Repository にさらに依存するとしたらこんな感じのコードになる ); }こういったコンストラクターに必要な実装クラスのインスタンスを設定してインスタンスを生成してくれるような処理を汎用的にしたものが DI コンテナーです。C# だと Unity とか DryIoc とかがあります。Java だと Spring Framework とかが有名ですね。私も就職した 2005 年くらいに Spring Framework や今はサポートも切れた Seasar2 で DI について初めて触れました。
単体テストも容易に
といういことで、下位のレイヤーの実装に引きずられなくなるので、結果としてモック実装を差し込むことが出来て単体テストも容易になります。素晴らしい!
実際単体テストって必須だよね
フットワーク軽くアプリを開発しようとしたら単体テストはほぼ必須です。MS でも Microsoft の DevOps への道のり にあるように、実行が遅い UI テストやクラスを結合した状態でのテストよりは、素早く開発サイクルを回すために単体テストをしっかりやるという話が触れられてます。
最初は育てる予定が無かったソフトウェアでも不慮の事故で育てることになったときに単体テストが出来ないと大変なので単体テスト可能なようにつくっておくことは大事だと思います。そのために DI コンテナ使ってレイヤー間はインターフェースを挟んで実装を差し替え可能なようにしておくと必要になったタイミングで単体テストを書くことが出来て幸せになれます。
まとめ
とまぁこんな感じで書きましたが個人的には DI(依存性の注入)は、単体テスト容易なソフトウェアを作るための現時点での現実解です。
単体テストは 100% 完全にする必要はないけど単体テストが出来ないように作るデメリットは大きすぎて、大きなソフトウェアでは私は単体テスト不可能に作る勇気はちょっとありません…。そして、最近のフレームワークのほとんどは DI コンテナを前提に作られてるので、その恩恵を受けつつ単体テストも出来るように開発するのが実装のためのオーバーヘッド(インターフェースを追加で定義しないといけない)も許容範囲で、一番楽できるという選択だと思います。
という考えから「DI (依存性注入) って何のためにするのかわからない人向けに頑張って説明してみる」に書いたように単体テストを容易にするために DI を前提とした形で作るといいよね!という記事が生まれました。
ということで DI 前提の作りにして単体テストをやろうと思ったときに、やることが出来る感じにコードを書いて、なるべく楽しましょう。
あ、あと頑張って書いたので、もしこの説明が抜けてるとか、こういう解釈のほうがいいとかいうのがあれば編集リクエストなどで教えてください m(_ _)m
- 投稿日:2020-07-10T18:27:33+09:00
C# DataGridView スクロールすると残像で文字がつぶれる
C# DataGridView スクロールすると残像で文字がつぶれる
うーん。。。原因は結局分からなかった。
でもとりあえず解決はした。デザイナー.csに書かれていた以下の行を削除したところスクロールしても残像はなくなった。this.Column1.DefaultCellStyle = dataGridViewCellStyle1;なぜに・・・?!
少し思い当たるのはコラムごとに色々設定値をいじっていた+データグリッドビュー全体の設定値も色々試しにいじっていたので、何か矛盾する設定値を設定していたことが原因のような気もする・・・
あんまり腑に落ちないけど。まぁ今詳しく調べる時間もないので(言い訳)とりあえず。
困ったらここ消そ。またちゃんと調べて追記しろよ自分~
- 投稿日:2020-07-10T17:49:31+09:00
DI コンテナは自分で new しないでフレームワークを探そう
思ったより↓の記事がバズった。
DI (依存性注入) って何のためにするのかわからない人向けに頑張って説明してみる自分で DI コンテナを new しないで
自分で DI コンテナのインスタンスを new して管理して、自分でコンテナからインスタンスを取得するようなコードは出来れば書かない方がいいです。
世の中に対応しているフレームワークがきっとあるので、それを使いましょう。車輪の再発明は勉強にはなるけど実際の製品コードでは書かない方がいいので気を付けましょう。因みに勉強のために DI コンテナを土台にしたオレオレフレームワークを組んでみるのは楽しいです。
C# の場合
C# だと ASP.NET Core を使っていれば、フレームワーク自体がどっぷりと DI コンテナを前提とした作りになっています。
C# で Web アプリ (Web API, gRPC, SPA(Blazor)) を作るときは、ASP.NET Core の流儀に従いましょう。
流儀に従っていれば DI コンテナを使うように自然になりますし、その方が便利です。ASP.NET Core は
Startup.cs
クラスの ConfigureServices メソッドの引数に渡されてくる IServiceCollection に色々なクラスを登録していきます。
ここで登録したクラスは Web API や Web ページを表すクラスに DI したりもできるし、コンテナ内にはフレームワークが提供する便利機能を持ったクラスも登録されているので、それを使う場合もコンストラクターの引数で受け取って使うことが出来て便利です。WPF/UWP/Xamarin.Forms は Prism というフレームワークを使うと DI コンテナまわりの機能もついてくるのでそれを使うのがいいと思います。
Java 系の場合
Java 系言語の場合は Spring Boot がいいんですかね?最近 Java 系言語を追いかけてないのですが…。
15 年前に Spring Framework や Seasar2 あたりで DI コンテナに初めて触れたので、懐かしいです。前の記事にはてなブックマークいっぱい
はてなブックマークのコメントをいくつかピックアップしてみたいと思います。
ちょっとしか読んでないけど、技術の歴史を踏まえないと正しい理解を持てないという典型に見える。 new Object()→Factoryパターン→DIという発展の中の最初の矢印しか説明してないのでDIを理解してない人に見えてしまう説明のときにはしょったのは事実。説明時には別にいらないと思ったので。
うしてテスト用途が主という誤った考えが生まれるんだよなぁ。 コンポーネントとコンポーネントの結合を疎にし、短くて簡素で柔軟なプログラムを生むのに使うのが正しいのだけど/それにつけてもDJイベントの楽しさよ静かな方が好きなので DJ のいるイベントは煩い印象なのでそんなに好きじゃない…。
コンポーネントとコンポーネントの結合を疎にするのは、実装クラスへの直接的な依存ではなくインターフェースを間に挟むことでお互いが実装に依存しないようになることで実現できる類のものに感じます。
短くて簡素なプログラムはメソッド分割やクラス分割。柔軟なプログラムも上の手段を使って、うまく設計した結果得られるもの。
そういう風に設計されたクラスのインスタンス組み立てるためには DI コンテナ使うと楽出来るし、単体テストもしやすくなるので好き。
個人的には単体テスト可能なコードを書く方が、設計が素晴らしいコードを書くのよりは優先度が高め。優先度が高いだけで設計が素晴らしいコードは大好きです。
いや、違くない?この説明だとFactoryでいいという話になるよね(筆者もテストのMockingが容易ならDIいらないと言ってしまってるのでその認識っぽい)。 重要なのは依存を隠蔽することだよね。どっかで記事書こう記事楽しみにしてます。
単体テストがしやすいコードを書いていく(もしくは保守性の高い設計を突き詰める)と DI コンテナを使わないと凄くメンドクサイので使ってる認識。
世の中のフレームワークは、割と DI コンテナを前提に作られていて、それは単体テストがしやすい保守性の高いコードを作るための現時点での最適解だという共通の認識があるからだと思ってます。(別の具象クラスへの)依存を隠匿するためには C#/Java などではインターフェースを挟んだり外部から実装を注入してもらうようにしないといけないから、それを省力化するための DI コンテナがあるように感じてる。
省力化が必要なほど大変じゃないなら DI コンテナいらないと思ってるけど現状は DI コンテナを使うのがベストだと思っています。
著者のプロフィールにちょっと引いてしまった。愛が重すぎる。これ重いかなぁ?プログラミング楽しいよ。C# はいい言語だよ。
- 投稿日:2020-07-10T16:50:41+09:00
Playを押すたびに"Processing Addressable Group"と表示される。
やあ
備忘です。
あなたのAddressableのソースは毎回[Play!]を押すたびにビルドされてます。
※私はビルドする必要があるかチェックしているのも含めて"ビルド"と呼んでます。おおきく3つ手順があります。
1.プレイモードの変更
2.プロファイルの設定
3.Addressableのビルド1.プレイモードの変更
以下のように変更して下さい。
Play Mode Script > Use Existing Build(requires build groups)2.プロファイルの設定
適当なフォルダにビルドパス、ビルドローカルパスを変更します。
3.Addressableのビルド
Addressableのソース?を変更したら毎回実行してください。
以上です。
- 投稿日:2020-07-10T16:07:48+09:00
ベクトルスコープ
using System; using System.Linq; using OpenCvSharp; public static class VectorScope { const double RY_R = +0.701; const double RY_G = -0.587; const double RY_B = -0.114; const double BY_R = -0.299; const double BY_G = -0.587; const double BY_B = +0.886; public static Point Rotation(int cx, int cy, double theta, double radius) { var x = cx + radius * Math.Cos(theta); var y = cy - radius * Math.Sin(theta); return new Point(Math.Round(x, MidpointRounding.AwayFromZero), Math.Round(y, MidpointRounding.AwayFromZero)); } public static (double ry, double by, double ec, double maxval) Calc(Scalar bgr) { var ry = RY_R * bgr[2] + RY_G * bgr[1] + RY_B * bgr[0]; var by = BY_R * bgr[2] + BY_G * bgr[1] + BY_B * bgr[0]; var ec = Math.Sqrt(Math.Pow(ry / 1.14, 2) + Math.Pow(by / 2.03, 2)); var maxval = Math.Sqrt(Math.Pow(RY_R / 1.14, 2) + Math.Pow(BY_B / 2.03, 2)); return (ry, by, ec, maxval); } public static Point Rgb2Point(int cx, int cy, double radius, Scalar bgr) { var (ry, by, ec, maxval) = Calc(bgr); var theta = Math.Atan2(ry / 1.14, by / 2.03); return Rotation(cx, cy, theta, ec / maxval * radius); } public static void DrawTarget(Mat img, int cx, int cy, int radius, Scalar bgr, double angle, string element) { var (_, _, ec, maxval) = Calc(bgr); var percent = ec / maxval; { var coeff1 = 0.40; var radius1 = radius * percent * (1 - coeff1 / 2); var radius2 = radius * percent * (1 + coeff1 / 2); var angle1 = Math.Round(angle - 10.0, MidpointRounding.AwayFromZero); var angle2 = angle1 + (10.0 * 2); var coeff2 = 0.28; var radius3 = radius * percent * (1 - coeff2 / 2); var radius4 = radius * percent * (1 + coeff2 / 2); var p1 = Rotation(cx, cy, angle1 * Math.PI / 180, radius1); var p2 = Rotation(cx, cy, angle1 * Math.PI / 180, radius2); var p3 = Rotation(cx, cy, angle1 * Math.PI / 180, radius3); var p4 = Rotation(cx, cy, angle1 * Math.PI / 180, radius4); Cv2.Line(img, p1, p3, Scalar.LightGray, 1, LineTypes.AntiAlias); Cv2.Line(img, p4, p2, Scalar.LightGray, 1, LineTypes.AntiAlias); var p5 = Rotation(cx, cy, angle2 * Math.PI / 180, radius1); var p6 = Rotation(cx, cy, angle2 * Math.PI / 180, radius2); var p7 = Rotation(cx, cy, angle2 * Math.PI / 180, radius3); var p8 = Rotation(cx, cy, angle2 * Math.PI / 180, radius4); Cv2.Line(img, p5, p7, Scalar.LightGray, 1, LineTypes.AntiAlias); Cv2.Line(img, p8, p6, Scalar.LightGray, 1, LineTypes.AntiAlias); Cv2.Ellipse(img, new Point(cx, cy), new Size(radius1, radius1), -angle2, 0, 5.0, Scalar.LightGray, 1, LineTypes.AntiAlias); Cv2.Ellipse(img, new Point(cx, cy), new Size(radius2, radius2), -angle2, 0, 5.0, Scalar.LightGray, 1, LineTypes.AntiAlias); Cv2.Ellipse(img, new Point(cx, cy), new Size(radius1, radius1), -angle2, 15, 20, Scalar.LightGray, 1, LineTypes.AntiAlias); Cv2.Ellipse(img, new Point(cx, cy), new Size(radius2, radius2), -angle2, 15, 20, Scalar.LightGray, 1, LineTypes.AntiAlias); } { var coeff1 = (0.714 * 5 / 100) / maxval; // 5IRE var radius1 = radius * percent * (1 - coeff1 / 2); var radius2 = radius * percent * (1 + coeff1 / 2); var angle1 = Math.Round(angle - 2.5, MidpointRounding.AwayFromZero); var angle2 = angle1 + (2.5 * 2); var p1 = Rotation(cx, cy, angle1 * Math.PI / 180, radius1); var p2 = Rotation(cx, cy, angle1 * Math.PI / 180, radius2); Cv2.Line(img, p1, p2, Scalar.LightGray, 1, LineTypes.AntiAlias); var p3 = Rotation(cx, cy, angle2 * Math.PI / 180, radius1); var p4 = Rotation(cx, cy, angle2 * Math.PI / 180, radius2); Cv2.Line(img, p3, p4, Scalar.LightGray, 1, LineTypes.AntiAlias); Cv2.Ellipse(img, new Point(cx, cy), new Size(radius1, radius1), -angle2, 0, 5, Scalar.LightGray, 1, LineTypes.AntiAlias); Cv2.Ellipse(img, new Point(cx, cy), new Size(radius2, radius2), -angle2, 0, 5, Scalar.LightGray, 1, LineTypes.AntiAlias); } { var radius1 = radius * percent; var p1 = Rotation(cx, cy, angle * Math.PI / 180, radius1); Cv2.PutText(img, element, new Point(p1.X, p1.Y - 18), HersheyFonts.HersheySimplex, 0.8, Scalar.LightGray, 1, LineTypes.AntiAlias); } } public static void DrawBackgroud(Mat img, int cx, int cy, int radius) { foreach (var i in Enumerable.Range(1, 5).Select(n => (1 / 5.0 * n))) { Cv2.Circle(img, cx, cy, (int)(radius * i), Scalar.DimGray, (i == 1) ? 2 : 1, LineTypes.AntiAlias); } foreach (var i in Enumerable.Range(0, 4).Select(n => n * 360 / 4)) { Point p2; p2 = Rotation(cx, cy, i * Math.PI / 180, radius); Cv2.Line(img, cx, cy, p2.X, p2.Y, Scalar.DimGray, 1, LineTypes.AntiAlias); // +Q(33)/-I(303) p2 = Rotation(cx, cy, (i + 33.0) * Math.PI / 180, radius * 0.88); Cv2.Line(img, cx, cy, p2.X, p2.Y, Scalar.DimGray, 1, LineTypes.AntiAlias); } foreach (var i in Enumerable.Range(0, 180).Select(n => n * 2)) { var p1 = Rotation(cx, cy, i * Math.PI / 180, radius * ((i % 10) == 0 ? 0.940 : 0.960)); var p2 = Rotation(cx, cy, i * Math.PI / 180, radius); Cv2.Line(img, p1, p2, Scalar.DimGray, (i % 10 == 0) ? 2 : 1, LineTypes.AntiAlias); } // M(60.8)/R(103.4)/Y(167.1)/G(240.8)/C(283.4)/B(347.1) Scalar[] BGR = { new Scalar(1.0, 0.0, 1.0), new Scalar(0.0, 0.0, 1.0), new Scalar(0.0, 1.0, 1.0), new Scalar(0.0, 1.0, 0.0), new Scalar(1.0, 1.0, 0.0), new Scalar(1.0, 0.0, 0.0) }; double[] ANGLE = { 60.8, 103.4, 167.1, 240.8, 283.4, 347.1 }; string[] ELEMENT = { "MG", "R", "YL", "G", "CY", "B" }; foreach (var ((bgr, angle), element) in BGR.Zip(ANGLE, Tuple.Create).Zip(ELEMENT, Tuple.Create)) { DrawTarget(img, cx, cy, radius, bgr, angle, element); } } public static void DrawPixels(Mat img, int cx, int cy, int radius, Scalar bgr) { var point = Rgb2Point(cx, cy, radius, bgr); var bgr_ = new Scalar(bgr[0] * 255, bgr[1] * 255, bgr[2] * 255); Cv2.Circle(img, point, 1, bgr_, -1, LineTypes.AntiAlias); } public static void Run() { const int COLS = 640; const int ROWS = 640; const int MARGIN = 10; var image = Cv2.ImRead("Mandrill.bmp"); var cx = COLS / 2; var cy = ROWS / 2; var radius = cy - MARGIN; var image_bg = new Mat<Vec3b>(ROWS, COLS, Scalar.Black); var data = new Vec3d[image.Height, image.Width]; image.ConvertTo(InputArray.Create(data).GetMat(), MatType.CV_64FC3, 1 / 255.0, 0); DrawBackgroud(image_bg, cx, cy, radius); var image_dst = image_bg.Clone(); foreach (var bgr in data.Cast<Vec3d>()) { DrawPixels(image_dst, cx, cy, radius, (Scalar)bgr); } Cv2.AddWeighted(image_dst, 1.0, image_bg, 0.4, 0.0, image_dst); Cv2.ImShow("input", image); Cv2.ImShow("output", image_dst); Cv2.WaitKey(0); } }・以下のサイトを参考にさせていただきました。
ベクトルスコープを描いてみる @linear_pcm0153
- 投稿日:2020-07-10T12:48:11+09:00
今日の気になる&調べた
Sasfariのタブを残しておいたりする代わりにここに記載・追記していこうと思う。
気になる
- ついに出た、「骨伝導の完全ワイヤレスイヤホン」! 音楽も外音も同時リスニング 骨伝導+ワイヤレスなんて欲しいに決まってる
- macOS Safariで簡単にmarkdownリンクをコピーできるExtensionを書いた Qiitaへの投稿を楽にするために入れた
調べた
LINE
- LINEログインしたときにLINE公式アカウントを友だち追加する(ボットリンク) | LINE Developers
- LINE Front-end Framework
- line-liff-v2-starter
- LINE Messaging API SDK | LINE Developers
- LINE の Bot 開発 超入門(前編) ゼロから応答ができるまで C# 編 - Qiita
- C#で開発するLINE Botアプリケーション (開発環境編) - pierre3のブログ
- Messaging APIリファレンス | LINE Developers
SwiftUI
- observableobject swiftui array 変更 - Google 検索
- How to push controller from ExtensionDelegate WatchKit
- SwiftUIにおけるForEach内からのBindingオブジェクトの渡し方
- Building watchOS App Interfaces with SwiftUI | Apple Developer Documentation
- SwiftUI: How to remove margin between views in VStack? - Stack Overflow
SF Symbols
C# on Mac
- macOS ではじめる ASP.NET Core x Entity Framework Core x PostgreSQL - Qiita
- チュートリアル: ASP.NET Core で Web API を作成する | Microsoft Docs ごめんなさい C#に関しては素人なんです。
GUID とか OTPとか
- 投稿日:2020-07-10T09:18:11+09:00
[C++] 引用符で囲まれていないプログラムパス(Unquoted Program Path) の脆弱性確認
やりたいこと
作成したアプリが、引用符で囲まれていないプログラムパス(Unquoted Program Path) の脆弱性に対して問題ないことを確認したい。
VisualStudio2019使用、C#/C++で書いたアプリに対してその脆弱性をどう確認すればよいか?調べてもパッとでてこなかったので、調べたことをメモ。
(恐らくこれで理解あってると思うが、誤りあれば指摘いただけるとありがたいです)引用符で囲まれていないプログラムパス(Unquoted Program Path) の脆弱性 とは?
CreateProcess
シリーズの関数を使ってプロセスを起動しようとしたときに起きる脆弱性。
CreateProcess
シリーズの関数というのは、下記の関数。→こちら
CreateProcessA
関数のページに、その脆弱性について、下記のように書いている。→こちら
これは例えば、
c:\program files\sub dir\programname.exeというexeをCreateProcessA関数で起動しようとしたときに、
関数の1個目の引数「lpApplicationName」をNULLにして、「lpCommandLine」に
c:\program files\sub dir\programname.exe
という文字列を入れて起動することができるが、こういう場合がNG.cppif( !CreateProcessA( NULL, // 1個目の引数lpApplicationName "c:\program files\sub dir\programname.exe",// 2個目の引数lpCommandLine NULL,NULL,FALSE,0,NULL,NULL,&si,&pi ) )その場合、CreateProcessA関数は
- c:\program.exe
- c:\program files\sub.exe
- c:\program files\sub dir\program.exe
- c:\program files\sub dir\programname.exe
という順番で解釈をして、(存在しなかったら次、また存在しなかったら次、という感じで)exeを実行しようとする、というのが
CreateProcess
シリーズの関数の仕様なので、もし、通常は存在しない
c:\program.exe
というexeが、悪意あるプログラムとしてそこに置かれていたら、programname.exe
を呼び出したかった実装者の意図とは関係なく、c:\program.exe
が呼ばれてしまう。という脆弱性。
大丈夫なパターンメモ
c:\program files\sub dir\programname.exe
を呼ぶときに、同じCreateProcessA
関数を使う場合でも、1個目の引数「lpApplicationName」をNULLにせずに、そこにプログラムのフルパスを入れて呼び出すこともできる。こういう場合はセーフ.cppif( !CreateProcessA("c:\program files\sub dir\programname.exe", // 1個目の引数lpApplicationName "/a /b", // 2個目の引数lpCommandLine(exeへの引数になる) NULL,NULL,FALSE,0,NULL,NULL,&si,&pi ) )その場合は、上のような問題は発生しない。
(Program.exeをC直下などに置いて試した結果、発生しなかった。)確認のしかた
確認① CreateProcess()シリーズの関数を使っているかどうか調べる
CreateProcess()シリーズの関数の仕様で、上のページに書いてあるようなUnquoted Program Path脆弱性が発生するので、
CreateProcess()シリーズ関数を使用していなければ問題なしとする。
- CreateProcessマクロ
- CreateProcessA関数
- CreateProcessAsUserA関数
- CreateProcessAsUserW関数
- CreateProcessW関数
確認② CreateProcess()を使っている場合の対処を実施
lpApplicationNameをNULLにして、lpCommandLineにパスを指定してプロセスを起動するようなコードになっていないか確認。
lpApplicationNameをNULLにしてなければOK。
lpApplicationNameをNULLにして、lpCommandLineでパスを指定していたらNG。その場合は、lpCommandLineのパスの指定の文字列を、「""」でかこってやる。
→こちらのページを参照。確認③ VisualStudio2019の静的解析で、その脆弱性にあたる指摘が出ていないことを確認する
静的解析の指摘「C6277」がその脆弱性に当たる指摘のため、静的解析を行い、C6277が出ないことを確認。
C6277とは
https://docs.microsoft.com/ja-jp/cpp/code-quality/c6277?view=vs-2019※今回は、下記の設定で静的解析を実施。(C#は関係ないと思うが、一応)
PJ種類 静的解析ルール C++ Microsoftネイティブ推奨規則 C++/CLI Microsoft混合(C++/CLR)推奨規則 C# Microsoftマネージド推奨規則 参照
C6277(Unquoted Program Path への静的解析指摘)
https://docs.microsoft.com/ja-jp/cpp/code-quality/c6277?view=msvc-160NULL application name with an unquoted path in call to CreateProcess
https://help.semmle.com/wiki/display/CCPPOBJ/NULL+application+name+with+an+unquoted+path+in+call+to+CreateProcessCWE-428: Unquoted Search Path or Element
https://cwe.mitre.org/data/definitions/428.html易し目の解説
http://securitychecklist.net/security/cyber-attack/Unquoted-Program-Path.html
- 投稿日:2020-07-10T08:52:18+09:00
【C#】throw式
★throw式が記述できる箇所は以下の3か所
1:ラムダ式、式形式メンバーの「=>」の右辺
2:Null合体演算子(??)の右辺
3:条件演算子の2,3つ目の引数
上記以外での記述はコンパイルエラーとなる。throw式記述場所//1:ラムダor式形式のメンバーの中 static void test1() => throw new NotImplementeException(); static string test2(string ex) { //2:Null合体演算子(??)の右辺 var a = ex as string ?? throw new ArgumentException(nameof(obj)); //3:条件演算子の2,3つ目の引数 return a.Length == 0 ? "enmpty" : a.Length < 5 ? "short" : throw new InvalidOperationException("too long"); }
- 投稿日:2020-07-10T02:36:37+09:00
[Unity Editor拡張]ゲーミングUnityを作った
はじめに
ゲーミングUnityとは、皆さんご存じのゲーミングPCが七色に光るアレのUnityバージョンです
Editor拡張を使用して作成しました
開発効率が上がるとかそういう内容は一切ありません結果
ダウンロードはこちら
unitypackageになっています
https://github.com/kyourikey/GamingUnityEditorExtension/releases
環境
- Windows 10
- Unity 2018.4.19f1
解説
やっている事はシンプルです
- ウィンドウを作成して表示
- 虹色グラデーション画像をスクロールさせながら表示
- UpdateでRepaintを呼び再描画
おわりに
短いコードで気軽に作れるので、皆さんもEditor拡張でUnityの見た目を自分好みにカスタマイズしちゃいましょう!
おまけ
以前私が書いたEditor拡張の記事です
今回作ったものと近い内容なので、興味が有ればこちらもどうぞ!
- UnityEditor拡張でグラフを描く際に使用したメソッドの解説
こんな感じのUIがUnity上に表示できるEditor拡張です
参考サイト
- Unity - スクリプトリファレンス - EditorWindow.Show
- Unity - スクリプトリファレンス - GUI.DrawTexture
- Unity - スクリプトリファレンス - EditorWindow.Repaint
- 投稿日:2020-07-10T00:20:45+09:00
[C#]struct制約のないジェネリック型の値から、Nullable<T>.Value(またはHasValue)にアクセスする
ジェネリック型を使用し、
struct
制約があればこんなコードですが、void Func<T>(T? val) where T :struct { Console.WriteLine(val.Value.GetHashCode()); } long? value = long.MaxValue; Func(value);これを制約なしでアクセスしたいわけです。
void Func<T>(T val) { Console.WriteLine(val.Value.GetHashCode()); ^^^^^ //アクセスできない } long? value = long.MaxValue; Func(value);実行時に
Nullable.GetUnderlyingType(typeof(T));
でSystem.Type
を取得してリフレクションでも取得できますが、もう少し効率の良さそうな方法を取りたいので、Expression式を使います。Expression式を使用したサンプルコード
using System; using static System.Linq.Expressions.Expression; static class InnerValueHashCode { public static int Get<T>(T value) => Impl<T>.Delegate(value); private static class Impl<T> { public static Func<T, int> GenerateFunc() { var nullableValue = Parameter(typeof(T), "nullableValue"); var underlyingType = Nullable.GetUnderlyingType(typeof(T)); var callExpression = Call( Property(nullableValue, typeof(T), "Value"), underlyingType.GetMethod("GetHashCode", Type.EmptyTypes)); var lambda = Lambda<Func<T, int>>(callExpression, nullableValue); return lambda.Compile(); } internal readonly static Func<T, int> Delegate = GenerateFunc(); } } long? value = long.MaxValue; InnerValueHashCode.Get(value);もちろん
Nullable<T>
以外を渡すと正しく動作しないので、事前にジェネリック型
のチェックは必要です。
- 投稿日:2020-07-10T00:20:45+09:00
[C#]struct制約のないジェネリック型の値から、Nullable<T>.Valueにアクセスする
ジェネリック型を使用し、
struct
制約があればこんなコードですが、void Func<T>(T? val) where T :struct { Console.WriteLine(val.Value.GetHashCode()); } long? value = long.MaxValue; Func(value);これを制約なしでアクセスしたいわけです。
void Func<T>(T val) { Console.WriteLine(val.Value.GetHashCode()); ^^^^^ //アクセスできない } long? value = long.MaxValue; Func(value);実行時に
Nullable.GetUnderlyingType(typeof(T));
でSystem.Type
を取得してリフレクションでも取得できますが、もう少し効率の良さそうな方法を取りたいので、Expression Treeを使います。Expression Treeを使用したサンプルコード
using System; using static System.Linq.Expressions.Expression; static class InnerValueHashCode { public static int Get<T>(T value) => Impl<T>.Delegate(value); private static class Impl<T> { public static Func<T, int> GenerateFunc() { var nullableValue = Parameter(typeof(T), "nullableValue"); var underlyingType = Nullable.GetUnderlyingType(typeof(T)); var callExpression = Call( Property(nullableValue, typeof(T), "Value"), underlyingType.GetMethod("GetHashCode", Type.EmptyTypes)); var lambda = Lambda<Func<T, int>>(callExpression, nullableValue); return lambda.Compile(); } internal readonly static Func<T, int> Delegate = GenerateFunc(); } } long? value = long.MaxValue; InnerValueHashCode.Get(value);もちろん
Nullable<T>
以外を渡すと正しく動作しないので、事前にジェネリック型
のチェックは必要です。また、
HasValue == true
の場合、Nullable.ToString()
とNullable.GetHashCode()
はValue
の値を返すので、サンプルコードそのままでは実用性は皆無です。更にいうと、
dynamic
を使えば瞬殺です。追記
もう少し実用的なサンプルコードを追記します。
ジェネリック型を受け取る
FormatableToString
メソッドが、T.ToString(string)
もしくはT.Value.ToString(string)
を呼び出すようにします。
ToString(string)
が無ければTypeInitializationException
を投げます。呼び出し元class Program { static void Main(string[] args) { FormatableToString(99999U, "X"); FormatableToString((uint?)99999U, "X"); FormatableToString(IntPtr.Zero, "X"); FormatableToString(UIntPtr.Zero, "X"); // throws System.TypeInitializationException } static void FormatableToString<T>(T value, string format) { string result; if (typeof(T).IsGenericType && typeof(T).GetGenericTypeDefinition() == typeof(Nullable<>)) { result = NullableHelper.ToString(value, format); } else { result = CallHelper.ToString(value, format); } Console.WriteLine(result); } }
NullableHelper
とCallHelper
の実装コードは長いので折りたたみusing System; using System.Runtime.CompilerServices; using static System.Linq.Expressions.Expression; static class CallHelper { public static string ToString<T>(T value) => Impl<T>.ToStringDelegate(value); public static string ToString<T>(T value, string format) => Impl<T>.ToStringWithFormatDelegate(value, format); private static class Impl<T> { public static Func<T, TResult> MakeCallFunc<TResult>(string name) => MakeCallFunc<TResult>(name, Type.EmptyTypes); public static Func<T, TResult> MakeCallFunc<TResult>(string name, params Type[] argTypes) { var value = Parameter(typeof(T), "value"); var targetMethod = typeof(T).GetMethod(name, argTypes); if (targetMethod == null) throw new MissingMethodException(typeof(T).FullName, name); var lambda = Lambda<Func<T, TResult>>( Call(value, targetMethod), value); return lambda.Compile(true); } public static Func<T, TArg, TResult> MakeCallFunc<TArg, TResult>(string name) { var value = Parameter(typeof(T), "value"); var arg1 = Parameter(typeof(TArg), "arg1"); var argTypes = new[] { typeof(TArg) }; var targetMethod = typeof(T).GetMethod(name, argTypes); if (targetMethod == null) throw new MissingMethodException(typeof(T).FullName, $"{name}({string.Join(",", argTypes.AsEnumerable())})"); var lambda = Lambda<Func<T, TArg, TResult>>( Call(value, targetMethod, arg1), value, arg1); return lambda.Compile(true); } internal readonly static Func<T, string> ToStringDelegate = MakeCallFunc<string>("ToString"); // T must has ToString(format) method. internal readonly static Func<T, string, string> ToStringWithFormatDelegate = MakeCallFunc<string, string>("ToString"); } } static class NullableHelper { public static string ToString<T>(T value) => Impl<T>.ToStringDelegate(value); public static string ToString<T>(T value, string format) => Impl<T>.ToStringWithFormatDelegate(value, format); private static class Impl<T> { static Impl() { System.Diagnostics.Debug.Assert(typeof(T).GetGenericTypeDefinition() == typeof(Nullable<>)); } public static Func<T, TResult> MakeCallFunc<TResult>(string name) => MakeCallFunc<TResult>(name, Type.EmptyTypes); public static Func<T, TResult> MakeCallFunc<TResult>(string name, params Type[] argTypes) { var nullableValue = Parameter(typeof(T), "nullableValue"); var underlyingType = Nullable.GetUnderlyingType(typeof(T)); var targetMethod = underlyingType.GetMethod(name, argTypes); if (targetMethod == null) throw new MissingMethodException(underlyingType.FullName, name); var lambda = Lambda<Func<T, TResult>>( Call( Property(nullableValue, typeof(T), "Value"), targetMethod), nullableValue); return lambda.Compile(true); } public static Func<T, TArg, TResult> MakeCallFunc<TArg, TResult>(string name) { var nullableValue = Parameter(typeof(T), "nullableValue"); var arg1 = Parameter(typeof(TArg), "arg1"); var underlyingType = Nullable.GetUnderlyingType(typeof(T)); var argTypes = new[] { typeof(TArg) }; var targetMethod = underlyingType.GetMethod(name, argTypes); if (targetMethod == null) throw new MissingMethodException(underlyingType.FullName, $"{name}({string.Join(",", argTypes.AsEnumerable())})"); var lambda = Lambda<Func<T, TArg, TResult>>( Call( Property(nullableValue, typeof(T), "Value"), targetMethod, arg1), nullableValue, arg1); return lambda.Compile(true); } internal readonly static Func<T, string> ToStringDelegate = MakeCallFunc<string>("ToString"); // T must has ToString(format) method. internal readonly static Func<T, string, string> ToStringWithFormatDelegate = MakeCallFunc<string, string>("ToString"); } }
- 投稿日:2020-07-10T00:14:43+09:00
Unity (C#) で小数点型の変数をintにキャストすると意図した値にならない(ように見える)ことがある
座標値をint型にする処理
int x = (int)hit.collider.gameObject.transform.position.x; Debug.Log("x1=" + hit.collider.gameObject.transform.position.x); Debug.Log("x2=" + x);Unityで書いてしまいそうな処理があります。
例えば、ボードゲーム系のゲームを作ろうとしたときに、クリックしたマスのボード上の座標を求めたいみたいなものがあると思います。hit.collider.gameObject.transform.position.xは2.0になっているつもりです。
intにキャストしているため、小数点以下は切り捨てになるはずなので2.5なら2や3.0なら3になってほしいわけですが..
このような結果になりました。
これは(原因を知らないと)おかしいと思ってしまいますね。
こういうので初心者さんが挫折してしまう理由にもなりかねないんですよね。ちなみに、キャストは四捨五入なんじゃないかとも言われそうですが
仕様としても切り捨てです。(厳密に言うと0に近い整数値)https://docs.microsoft.com/ja-jp/dotnet/csharp/language-reference/builtin-types/numeric-conversions
原因を調べる (ToString)
初心者だと、なかなか発想が浮かびにくいですが、そもそもこの値は2.0じゃないのではないかという発想になりますが、これを検証するのは知らないとなかなか難しいです。
デバッガでステップ実行しても、デバッガの表示も
ToString()
での値になるので生の値がわかりません。ちなみに、Unityのposition.x などは float型 つまり
System.Single
型です。ドキュメントを見てみると、ToString()は ToString("G")の省略形のようですね。
"G" を見てみると、7桁の精度しかないようです。
System.Single
型のドキュメントを見ると、既定では、戻り値の精度は7桁のみですが、内部的には最大9桁が保持されます。
とあるので、9桁を出して見ると..
Debug.Log("x1=" + hit.collider.gameObject.transform.position.x.ToString("G9")); Debug.Log("x2=" + x);これで謎が少し解決しそうですね。
ということで表示上は2になってしまうが、実は1.99999967だったみたいです。
ちなみに
Debug.Log("x2=" + x);というのも、実は
ToString
が勝手に補完されます。Debug.Log("x2=" + x.ToString());ところで、ToString()はなんで切り上げになったのか
https://docs.microsoft.com/ja-jp/dotnet/standard/base-types/standard-numeric-format-strings
精度指定子によって結果文字列内の小数部の桁数を制御する場合、結果文字列では無限に正確な結果に最も近い表現可能な結果に丸められた数値が反映されます。 同じように近い表現可能な結果が 2 つある場合は、次のようになります。
.NET Framework および .NET Core 2.0 までの .NET Core の場合、最下位の数字が大きい方の結果が選択されます (つまり、MidpointRounding.AwayFromZero が使用されます)。
.NET Core 2.1 以降の場合、ランタイムでは最下位の数字が同一である結果が選択されます (つまり、MidpointRounding.ToEven が使用されます)。とあります、
float f = 1.99999976f; Console.WriteLine(Math.Round(f, 6, MidpointRounding.AwayFromZero));というような処理が行われていると想像できます。
つまり丸められて切り上げになったんですね。つまり
class Program { static void Main(string[] args) { float f = 1.99999976f; Console.WriteLine(f); Console.WriteLine((int)f); Console.WriteLine(f.ToString("G9")); } }は
2 1 1.99999976という結果になります。
そもそも、そんな細かい値を使うの?
と思うかもしれませんが、UnityでGameObjectを作ったら、座標がランダムっぽい値で生成されるときがあります。
これをとくに気にせずに親のオブジェクトとして
Instantiate(guide , new Vector3(2, 0, 0) , Quaternion.identity, guideList.transform);のようなオブジェクトを作ると (2, 0, 0) はグローバル座標なので
生成後はこのようなローカル座標になります。
Unityはグローバル座標ではなく、どうもローカル座標から足し引きで求めているようで、本題のような微妙な座標値になることがあります。
なので上の座標値を (0,0,0) にするだけでも本題のバグが発生しない可能性が高いです。
ちなみに
transform.position
はグローバル座標で
transform.localPosition
は ローカル座標です。(インスペクタに表示されるのもこっち)まとめ
(int)
でキャストして切り捨てにすると意図しない挙動になる場合があるのでfloat f = 1.99999976f; int x = (int) Math.Round(f);のように四捨五入するのが安全かなと思います。