.NET Framework 3.5環境で nameof 演算子っぽいことする
最近仕事でこんな感じのメソッドを定義して使っています。
public static class Utility { private static Dictionary<string, string> exprCache = new Dictionary<string, string>(); public static string NameOf<T>(Expression<Func<T>> expr) { string exprString = expr.ToString(); string name; if (!exprCache.TryGetValue(exprString, out name)) { var body = expr.Body; if (body is ConstantExpression) { object value = ((ConstantExpression)body).Value; if (body.Type == typeof(Type)) { string typeName = value.ToString(); int backqIndex = typeName.IndexOf('`'); if (backqIndex >= 0) typeName = typeName.Substring(0, backqIndex); typeName = typeName.Substring(typeName.LastIndexOf('+') + 1); typeName = typeName.Substring(typeName.LastIndexOf('.') + 1); name = typeName; } else name = value != null ? value.ToString() : (string)null; } else if (body is MemberExpression) name = ((MemberExpression)body).Member.Name; else if (body is MethodCallExpression) name = ((MethodCallExpression)body).Method.Name; else throw new ArgumentException(exprString); exprCache[exprString] = name; } return name; } }
使い方は簡単。
public class Program { private static void Main(string[] args) { // リテラルはそのまま出力されます。 // nameof 演算子ではそもそもリテラルを囲うことができません。 Console.WriteLine(Utility.NameOf(() => "magic string")); // => "magic string" // 定数の場合はILへのコンパイル時に完全に置き換えられる為、定数名を出力できません。 const string constString = "const string"; Console.WriteLine(Utility.NameOf(() => constString)); // => "const string" // 型名が欲しい場合は、typeof演算子を使います。 // 構文上の制限による苦肉の策です。 Console.WriteLine(Utility.NameOf(() => typeof(MetaVar)); // => "MetaVar" Console.WriteLine(Utility.NameOf(() => typeof(Klass)); // => "Klass" // ==== ここから下は nameof 演算子と近い挙動を示します ==== // 列挙型を変数に入れるとNameOfメソッドは変数名を返します。 // これは列挙型に限らず、ローカル変数は全て変数名を返します。 var hoge = MetaVar.Hoge; Console.WriteLine(Utility.NameOf(() => hoge)); // => "hoge" // 列挙型を直接指定すると列挙型の名前を返します。 Console.WriteLine(Utility.NameOf(() => MetaVar.Fuga)); // => "Fuga" // klass はローカル変数なので、変数名を返します。 var klass = new Klass(); Console.WriteLine(Utility.NameOf(() => klass)); // => "klass" // プロパティの場合はプロパティ名を返します。 Console.WriteLine(Utility.NameOf(() => klass.Number)); // => "Number" // メソッドの場合はメソッド名を返します。 Console.WriteLine(Utility.NameOf(() => klass.Echo())); // => "Echo" } public enum MetaVar { Hoge, Fuga, Piyo } public class Klass { public int Number { get; set; } public string Echo() { return "I'm a instance of Klass"; } } }
今のところArgumentException
が投げられる場面には遭遇してません。
本来のnameof
演算子とは違ってコンパイラがよしなにするものではなく、式木の操作は動的な実行です。
定数はコンパイル時に置換されてしまっていますので、Utility.NameOf
メソッドはその違いを認識できません。
そのため、Utility.NameOf(constString)
とnameof(constString)
では実行結果が違います。
とはいえ、多少違いはあるものの、そこそこ実用できるのでいいと思います(こなみ)
そもそも C# 6.0/VB 14.0 の機能であって、.NET Framework 3.5はあんまり関係ないですね。
どちらかと言うとVisual Studioのバージョンが古いときに使えるtipsみたいな。
あと、注意点としては、式木の計算は結構重いです。
その対策として、上ではキャッシュ機構を組み込んでいます。
どれくらいキャッシュの効果があるかはベンチ取ってないので分かりませんが、対策なしは止めましょう。
INotifyPropertyChanged
の実装など、比較的呼び出される頻度が高いと効いてきそうです。
以後は特に指定のない限りJSはES6記法で書いていこうと思います。
Intlオブジェクトというものを知りました。
国際化表記における各種変換を取りまとめるそこそこ大規模なオブジェクトらしい。
Collator
DateTimeFormat
NumberFormat
の3つのサブオブジェクトを持つ模様。
それぞれインスタンス化して使用するっぽい。
(() => { let localeOption = { style: "currency", currency: "jpy" }; let locale = new Intl.NumberFormat("ja-JP", localeOption); console.log(locale.format(1234500)); // => ¥1,234,500 })();
便利。
第二引数のoptionにcurrencyDisplay
を指定すると表記方法が変わるっぽい。
currencyDisplay: "symbol"
がデフォルトで、他にcode
とname
があるらしい。
symbol
の場合の記号が半角とか全角とか、その辺は実装依存っぽい。
とか思ってたら
Number#toLocaleString
とか、他の代表的なオブジェクトにメソッドとして同等機能が提供されていた。
new
はオーバーヘッドも大きいし*1、特別な理由がない限りは基本こっちですね。
*1:実測はしていませんが
グローバルIPアドレスを得る
今まではipifyを使っていました。
> Invoke-WebRequest api.ipify.org | Write-Host
125.203.***.***
非常にシンプルで使用制限もなく応答速度も問題なくて重宝していました。
これで終わりでいいんですが、みんな大好きStackOverflowのとあるスレッドで有用なやり取りがあったので。
Getting my public IP via API - Stack Overflow
どうやらipinfo.ioというのがあるようで、「1日1000リクエストまで」という制限があるようですが*1柔軟性が高いです。
オプションを何もつけなければJSONでデータが返ってきます。
> Invoke-WebRequest ipinfo.io | Write-Host { "ip": "125.203.***.***", "hostname": "i125-203-(中略).ap.plala.or.jp", "city": "", "region": "", "country": "JP", "loc": "35.***,139.***", "org": "AS4713 NTT Communications Corporation" }
まぁ使い方は公式サイトにでかでかと書かれてるのでわざわざここで説明するまでもないんですが、JSONのプロパティがオプション名になってます。
> Invoke-WebRequest ipinfo.io/ip | Write-Host 125.203.***.*** > Invoke-WebRequest ipinfo.io/country | Write-Host JP
特に凝ったことするのでなければipify, ちょっと別の情報も使いたいってときはipinfo.ioって具合に使い分けましょうかね。
*1:無料プランでの話なので、有料プランを選べばもちろん制限は緩くなります。
OSXのバージョンを確認する方法
ただ単に今駆動しているOSXのバージョンを確認するだけなら
$ sw_vers ProductName: Mac OS X ProductVersion: 10.10.5 BuildVersion: 14F1605
で終わりです。
今回はとあるパーティションにインストールされているOSXのバージョンを外部から確認する方法です。
ある方から起動できなくなったHDDにインストールしたOSXのバージョンを確認してほしい、との依頼があったために調べて知った方法です。
要するに一部のLinuxディストリで言うcat /etc/*-release
のようなものです。
まずは目的のパーティションに移動します。
$ cd /Volumes/Machintosh\ HD/
その上で、そのパーティション上の/System/Library/CoreServices/
の中にあるSystemVersion.plist
にバージョン情報が書かれています。
$ cat System/Library/CoreServices/SystemVersion.plist <?xml version="1.0" encoding="UTF-8"?> <!DOCTYPE plist PUBLIC "-//Apple//DTD PLIST 1.0//EN" "http://www.apple.com/DTDs/PropertyList-1.0.dtd"> <plist version="1.0"> <dict> <key>ProductBuildVersion</key> <string>14F1605</string> <key>ProductCopyright</key> <string>1983-2016 Apple Inc.</string> <key>ProductName</key> <string>Mac OS X</string> <key>ProductUserVisibleVersion</key> <string>10.10.5</string> <key>ProductVersion</key> <string>10.10.5</string> </dict> </plist>
ちなみに、ここのXMLを書き換えることで「このMacについて」で表示されるバージョン情報を偽装することができるようです。
参考: Tutorial: Change the OS X Version by modifying systemversion.plist