スキップしてメイン コンテンツに移動

投稿

[C#] CILの算術演算命令

C#をコンパイルするとCILになりますが、CILの情報は結構少ないので、メモがてらに書いておきます。基本たとえば1 + 10とするC#コードがあったとして、これをCILにすると以下のようなものに論理的にはなります。ldc.i4.1 ldc.i4.s 10 add 逆ポーランド記法とかを知っている人ならどういう変換をされたかわかるかと思います。
C#での+の数値の加算はCILではaddになりますが、このaddはスタックにある2つの値を加算するものになります。
日本語で言うと「1 足す 10」は「1と10を読み込んで、読み込んだ2つの値を足す」になります。
どうしてこのような変換をするかとかは逆ポーランド記法を調べたら利点がわかるかと思いますので割愛。ちなみに、先ほど「以下のようなものに論理的にはなります」と言いましたが、実際のところは1 + 10ぐらいはコンパイラーが最適化してldc.i4.s 11になってしまうことがあります。命令一覧スタック操作命令動作nop何もしないdupスタックのトップの値を複製popスタックのトップの値を破棄 定数読み込み命令動作ldc.i4 {value}int32な{value}を読み込むldc.i4.s {value}int8な{value}を読み込むldc.i4.m1-1を読み込むldc.i4.00を読み込むldc.i4.11を読み込むldc.i4.22を読み込むldc.i4.33を読み込むldc.i4.44を読み込むldc.i4.55を読み込むldc.i4.66を読み込むldc.i4.77を読み込むldc.i4.88を読み込むldc.i8 {value}int64な{value}を読み込むldc.r4 {value}float32な{value}を読み込むldc.r8 {value}float64な{value}を読み込む ldc.i4.m1~ldc.i4.8は短縮命令です。算術演算命令動作add加算(+)sub減算(-)mul乗算(*)div除算(/)div.un符号無し整数の除算のときに使われるrem乗除(%)rem.un符号無し整数の乗除のときに使われるneg符号反転(-) ビット演算命令動作and論理積(&)or論理和(|)xor排他的論理和(^)notビット反転(~)shl左シフトshr右シフトshr.un符号無し右シ…

Azure DevOpsに改名してOSSなら無料/時間無制限ビルドになった旧VSTSを試す

今日の朝にVSTSがAzure DevOpsに改名し、OSS向けのビルド枠が時間無制限ビルドになったと聞いてさっそく試してみました。

値段とかそのあたりのことはここに書いてます。

そこで、今回CIビルドを試すのはこないだ作ったばかりのSharedPropertyにします。ちょうどテストコード書いてあるしね。

 Github Marketplaceから登録
GithubのツールバーのとこにあるMarketplaceを選択します。
出てきた画面でAzureと検索するとそれっぽいのが出てくるのでそれをクリックしてください。
 するとAzure Pipelinesのページに行くのでSet up a planを選択します。
選択したらスクロールされるので、Freeを選択しつつInstall it for freeと選択してください。publicなら並列10タスクまで無制限にビルドさせてやるぜ!って書いてたりします。
選択したら最後の確認画面になります。
先ほどの画面を進んでいくとAzure DevOpsのほうで対象となるリポジトリを選択する画面に出てきますので、今回はSharedPropertyを選択しました。無料なのでAll repositoriesを選択してもよかったのですが、使わないものを登録しても意味がないので今回は1つだけです。
次にAzure DevOpsでのプロジェクトを選べと出てくるので、新しく作成することにしました。 そしたらそれっぽい画面になるので、今回使うSharedPropertyリポジトリを選択しましょう。 リポジトリを選択したら、おすすめのテンプレートが表示されます。 この段階で自分はお勧めにしたがってしまいましたがそれが罠でした……(まぁ最初はお勧めでいいと思います) 次にAzure pipelineの設定ファイル(テンプレ)が登場するので、脳死でSave and runを押しましょう。 設定ファイルをブランチにcommitする際のメッセージとか選べるらしいですが、今回は脳死でSave and run。 押したら、やっとそれっぽい画面になったので気ままに待ちましょう。 UWPで.NET Standard 2.0を使うときはWindowsコンテナーじゃないといけなかった はい。。。
さきほどのデフォルトのテンプレはVS2017-Win2016とい…

Xamarin.AndroidはViewのClickリスナーを複数登録できる話

Androidアプリ作ってる人にView.setOnClickListenerを使ったことがない人はいないかと思います。
今日はそんなsetOnClickListenerな話です。※ViewのクリックをハンドリングすることをClickリスナーと表現しておきます。(AndroidとXamarin.Androidで少し名前違うので)Android APIのClickリスナーは1つしか登録できないsetOnClickListenerの名前から分かりますが、setOnClickListenerメソッドで登録できるOnClickListenerは1インスタンスしか登録できません。
そう、addとかできないんです。と、言っても名前詐欺の可能性も否定できないのでViewクラスのソースを見ましょう。View.setOnClickListenerのソースはgooglesourceを頑張ったら見れます。Githubではファイルサイズが大きすぎてRaw表示のみでした。
そこで肝心のソースですが、以下のようになっています。public void setOnClickListener(@Nullable OnClickListener l) { if (!isClickable()) { setClickable(true); } getListenerInfo().mOnClickListener = l; } getListnerInfo()で返却されるオブジェクトのフィールドにリスナーをsetしていることがわかります。(Javaにはプロパティがないのでフィールドに確定されます)
そのため、getListenerInfo()のインスタンスが1つに収束しているならば、リスナーは1つしか設定できない機構であることが証明できます。そこで肝心のView.getListenerInfoのソースですが、以下のようになっています。ListenerInfo getListenerInfo() { if (mListenerInfo != null) { return mListenerInfo; } mListenerInfo = new ListenerInfo(); return mListenerInfo; …

[C#]readonly structの値をメンバーメソッドを呼んで変更しているように見せる方法

C#のreadonly structはフィールドすべてにreadonly制約を掛けることによって防衛的コピーを防げる素晴らしい機能ですが、その反面readonlyなため値の書き換えができなくなります。
(※C#コードコンパイル時の話、リフレクション先輩のことは考えないでください) readonly struct ReadOnlyValue<T> { public readonly T Value; public ReadOnlyValue(T value) { Value = value; } } class Program { static void Main(string[] args) { var value = new ReadOnlyValue<int>(1); Console.WriteLine(value.Value); // 1 value.Method(); // たとえばここで10を出力させたいとき1つ上の行のvalue.Method();のMethodはどう定義したらいい? Console.WriteLine(value.Value); } } たとえば、このようなコードがある場合readonly structなのでメソッドを呼ぶだけでは値は変わらないはずです。(防衛的コピーも起きない)しかし、値を変える方法は存在します。さぁどうやったら変えれるでしょうか?っていう話です。まぁ、焦らさずに答えを出します。 static class ReadOnlyValueExtension { public static void Method(ref this ReadOnlyValue<int> value) { value = new ReadOnlyValue<int>(10); } } このような拡張メソ…

[C#]構造体の防衛的コピーについて

以前すこし話に触れた構造体の防衛的コピーについてCIL的に見ていこうという記事です。以前触れた記事: [C#]Big Size Structが値コピーでつらいならin引数で値コピーしなければいいじゃない!! < それ本当?防衛的コピーとは構造体の値がreadonlyではない場合に、その構造体を保持している変数にreadonly制約があるとその変数の構造体のメソッドを呼び出すと構造体の値が変わっていないことが保証されないので、防衛的に構造体の値をコピーしてreadonly制約な変数の値が変わらないようにするというコンパイラーの親切な行いのことを言います。ちょっとわかりにくいので例を:struct Struct { public void Method(){} } class Class { private readonly Struct readOnlyStruct = default; private Struct writableStruct = default; public void Method() { readOnlyStruct.Method(); writableStruct.Method(); } } Struct#Methodを呼んでいるClass#Methodに着目してみましょう。writableStruct.Method();ではインスタンス変数writableStructのStruct#Methodが呼び出されています。writableStructは値が変わることがあるインスタンス変数ですのでここでの呼び出し時には防衛的コピーは発生しません。しかし、readOnlyStruct.Method();ではreadonlyなインスタンス変数readOnlyStructのStruct#Methodが呼び出されています。readOnlyStructは値が変わってほしくありませんが、Struct#Methodの呼び出しによって値が変わらないという保証がありません。そこで、防衛的に値をコピーして、readOnlyStructの値が変わらないように(コンパイラーが)しているのです。防衛的コピーの例readonlyインスタンス変数の場合struct Struct …

[C#]SharpLabを使ってCILを見る

前回・前々回とCILを見るツールを紹介してきたので、SharpLabを紹介しないわけにはいかないだろうということで紹介しておきます。

前回: [C#]ILSpyを使ってCILを見る
前々回: [C#]ReSharperを使ってCILを見るSharpLabはWebサイト上でC#のコードがコンパイル後どのようになるか確認できるサイトです。

たとえば、この画像のようにC#コードをコンパイル→デコンパイルした結果を見てみることができたり

この画像のようにC#コードをコンパイルしCILになった姿を見てみることができます。他にも便利機能が多々あって、開発中の最新ブランチのコンパイラーを試したり、C#/CIL以外にもJIT後のアセンブリが見えたり、Debug/Releaseビルドを選択できたりします。さすがにJIT後のアセンブリは(自分には)読めないですが、Releaseビルドの最適化具合を雰囲気で味わうことができるのは便利ですね。ただの紹介記事だとつまらないので、最近CILの勉強を頑張ってるのでそれのアウトプット。さきほどの画像のC#コードusing System; public class C { public void M() { } } これのDebugビルド時のCILを見ると.class private auto ansi '<Module>' { } // end of class <Module> .class public auto ansi beforefieldinit C extends [mscorlib]System.Object { // Methods .method public hidebysig instance void M () cil managed { // Method begins at RVA 0x2050 // Code size 2 (0x2) .maxstack 8 IL_0000: nop IL_0001: ret } // end of method C::M .method public hideby…

[C#]ILSpyを使ってCILを見る

前回の記事でReSharperを使ってCILを見てみましたが、ReSharperを利用すると該当ファイルのCILしか見れず、アセンブリ情報などのCILは見れませんでした。
前回記事:[C#]ReSharperを使ってCILを見るそこで、ReSharper購入以前に使っていたILSpyを使って見ようと思ったらVisual Studio Extensionがあるのを今更発見したので紹介しておきます。インストールは簡単でVisual Studioのツールバーのツール=>拡張機能と更新プログラム=>オンラインからILSpyを検索して出てきたものをインストールするだけです。インストールできたらソリューションエクスプローラーのプロジェクトを右クリックするとILSpyで開く項目があるのでクリックしましょう。クリックしたらILSpyのWindowが開くのでCILを眺めることができます。と言ってもアセンブリで眺めたい情報ってあまりないかなという感じですね。.assembly SharedProperty.NETStandard { /*略*/ .hash algorithm 0x00008004 // SHA1 .ver 1:0:0:0 } .module SharedProperty.NETStandard.dll // MVID: {908E02B5-14DD-4164-8C84-C47735CD9DCA} .corflags 0x00000001 // ILOnly たとえば、SharedProperty.NETStandardはこんな感じのアセンブリ情報ですけど、見た目そのままの意味です。
.NET IL Assemblerという本には.assembly extern { auto }みたいな感じでバージョン自動解決なアセンブリインポート?というのができるみたいな感じに書いてありましたけどそういった記述は見つけることができず…
ILSpyでアセンブリのReferencesで参照アセンブリがコメントで書かれてるのでILSpyが自動判別してる…?(未確定情報)また、ILSpyなら名前空間の情報も見れます。(たいした情報ないですけど).namespace SharedProperty.NETStandard.Storage { …