読者です 読者をやめる 読者になる 読者になる

secretbase.log

Don't mix unrelated changes.

例外処理は重いのでEnum.TryParseを用いたほうが良い

C# .NET

定義した Enum のメンバーに例えば入力した文字列が合致した場合に変換するといったケースはよくあります。

そういった場合に使えるメソッドが標準で用意されています。

文字列表現を Enum に変換するメソッド

  • Enum.Parse

    • Parse は戻り値として変換結果を返します。変換出来ない場合は、例外を返すメソッドです。
  • Enum.TryParse

    • TryParse は変換出来ない場合は、戻り値として bool を返します。

使用例

ここでは Enum として元素記号(H, He, Li, Be...と元素番号順)を定義します。

  • 入力
    • 入力はなんでも良いのですが、あらかじめAからZのアルファベットの文字列配列を用意し、AからZまで順番に元素記号に変換できるかを試みます。
    • つまり、Aに該当する元素記号は存在せず、Bに該当する元素記号は存在するといった具合です。
      • 半分くらいが存在しないケースになります。
  • 出力
    • 存在した場合はそのメンバーと存在しない場合は 0 を返す、という仕様とします。

ChemicalElementsクラスは下記となります。

static class ChemicalElements
{
    /// <summary>
    /// 元素記号
    /// </summary>
    public enum SymbolsOfElements
    {
        H = 1, He,
        Li, Be, B, C, N, O, F, Ne,
        Na, Mg, Al, Si, P, S, Cl, Ar,
        K, Ca, Sc, Ti, V, Cr, Mn, Fe, Co, Ni, Cu, Zn, Ga, Ge, As, Se, Br, Kr,
        Rb, Sr, Y, Zr, Nb, Mo, Tc, Ru, Rh, Pd, Ag, Cd, In, Sn, Sb, Te, I, Xe,
        Cs, Ba, La, Ce, Pr, Nd, Pm, Sm, Eu, Gd, Tb, Dy, Ho, Er, Tm, Yb, Lu, Hf, Ta, W, Re, Os, Ir, Pt, Au, Hg, Tl, Pb, Bi, Po, At, Rn,
        Fr, Ra, Ac, Th, Pa, U, Np, Pu, Am, Cm, Bk, Cf, Es, Fm, Md, No, Lr, Rf, Db, Sg, Bh, Hs, Mt, Ds, Rg, Cn, Nh, Fl, Mc, Lv, Ts, Og,
    };

    /// <summary>
    /// Enum.Parse による実装
    /// </summary>
    /// <param name="name"></param>
    /// <returns></returns>
    static public SymbolsOfElements GetSymbolParse(string name)
    {
        SymbolsOfElements symbol;
        try
        {
            symbol = (SymbolsOfElements)Enum.Parse(typeof(SymbolsOfElements), name);
        }
        catch
        {
            symbol = 0;
        }
        return symbol;
    }


    /// <summary>
    /// Enum.TryParse による実装
    /// </summary>
    /// <param name="name"></param>
    /// <returns></returns>
    static public SymbolsOfElements GetSymbolTryParse(string name)
    {
        SymbolsOfElements symbol;
        Enum.TryParse(name, out symbol);
        return symbol;
    }
}

上記クラスを利用するクラスは下記のような形です。

class Program
{
    static void Main(string[] args)
    {
        Stopwatch sw = new Stopwatch();
    
        // A から Z までの文字列配列を作成する
        var alphabets = Enumerable.Range(0, 26).Select( n => ((char)('A' + n)).ToString() );

        // TryParse による処理
        Console.WriteLine("処理開始 [TryParse実装] ...");
        sw.Start();
        for (int i = 0; i < 100; i++)
        {
            foreach (var alphabet in alphabets)
            {
                var symbol = ChemicalElements.GetSymbolTryParse(alphabet);
            }
        }
        sw.Stop();
        Console.WriteLine($"処理時間: {sw.Elapsed}\n");


        // Parse による処理
        Console.WriteLine("処理開始 [Parse実装] ...");
        sw.Restart();
        for (int i = 0; i < 100; i++)
        {
            foreach (var alphabet in alphabets)
            {
                var symbol = ChemicalElements.GetSymbolParse(alphabet);
            }
        }
        sw.Stop();
        Console.WriteLine($"処理時間: {sw.Elapsed}\n");

        Console.Read();

    }
}

実行結果

処理開始 [TryParse実装] ...
処理時間: 00:00:00.0036813

処理開始 [Parse実装] ...
処理時間: 00:00:05.8251481

いずれも同じ結果となる処理が、TryParseの場合は、4ms 未満に対し、Parseの場合は、5.8秒となりました。 1500倍くらい差がありますね。

例外処理は重いので Enum.TryParse を用いましょう。

コレクションをソートして取り出す際にインデックスつける

C# Python

コレクションから要素を取り出していく際に、連番をつけたいことがあります。

例えば下記のような文字列の配列を、ソートしてインデックスとともに要素を取得したい、といった場合のTipsです。

"Banana", "Apple", "Melon"

ソートし取り出す際にインデックスつける

C#

C# の場合は、 Selectメソッドにオーバーロードがあり、それを用いることで実現できます。

string[] fruits = { "Banana", "Apple", "Melon" };

foreach(var item in fruits.OrderBy(n => n).Select((v, i) => new {v, i}))
{
    Console.WriteLine($"{item.i} {item.v}");
}
0 Apple
1 Banana
2 Melon

Python

Pythonでは 組み込み関数である enumerate を使います。 enumerateはインデックスと要素をタプルで返してくれます。

fruits = ["Banana", "Apple", "Melon"]

for index , item in enumerate(sorted(fruits)):
    print(index, item)
0 Apple
1 Banana
2 Melon

いずれも、シンプルに実装できますね。

参考

名前空間の名前

C# .NET

C# の場合、namespace はどのように付けたら良いか、以前調べた。

https://msdn.microsoft.com/ja-jp/library/ms229026(v=vs.100).aspx

から抜粋。

一般的な形式

 <Company>.(<Product>|<Technology>)[.<Feature>][.<Subnamespace>]

説明

  • 名前空間名には、プリフィックスとして企業名を付け、各企業の名前空間の名前とプリフィックスが重複しないようにしてください。
  • 名前空間の第 2 レベルには、バージョンに依存しない、安定した製品名を使用してください。
  • 企業内のグループ名は変更されることが多いため、名前空間の階層構造での命名基準として組織の階層構造を使用することは避けてください

なるほど、名前空間に、企業内の部署名をつけてしまうのは悪手のようだ。

説明(2016 10月更新版)

更新されていたことに気づいたので追記。

https://msdn.microsoft.com/ja-jp/library/ms229026(v=vs.110).aspx

✓は守るべき内容、Xは避けるべき内容。 機械翻訳があれなので、意訳して掲載。

  • ✓ 異なる会社が名前空間を同じにすることを防ぐために、名前空間名に会社名を付けること。
  • 名前空間名の第2レベルでは、バージョンに依存しない安定した製品名を使用してください。
  • X 企業内のグループ名は有効期間が短い傾向があるため、名前空間の階層内の名前の基準として組織階層を使用しない。
    • 関連技術のグループの周囲の名前空間の階層で整理すると良い。
  • pascal 表記を使用し、名前空間をピリオドで区切る (例: Microsoft.Office.PowerPoint)。 ブランド名はこの限りではなく逸脱しても良い。
  • ✓ 適切な場所に複数系の名前空間名を使用することを検討してください。
    • 例えば、System.Collection の代わりに System.Collections を使用します。ただし、ブランド名や頭字語はこの規則の例外です。例えば、System.IOs ではなく System.IO を使用します。
  • X 名前空間とその名前空間の型に同じ名前を使用しないでください。
    • たとえば、名前空間名としてDebugとして、同じ名前空間にDebugという名前のクラスも指定しないようにする。コンパイラに対し、型を完全修飾する必要がでてきます。

Windows 10 と Windows 8.1 を判定する

.NET Windows C#

動作しているOSのバージョンを取得して判定する必要があり、 Environment.OSVersion を用いて Major と Minorで判定するようにサンプルコードを動作させてみました。

しかし、Windows 10 でも Windows 8.1 でも Major が 6 のまま。

Windows 10 では majorは 10 のはず...。

サンプル

static void Main(string[] args)
{
    var os = Environment.OSVersion;

    Console.WriteLine("Version Information:");
    Console.WriteLine("   Major: {0}", os.Version.Major);
    Console.WriteLine("   Minor: {0}", os.Version.Minor);
}

解決策

過去の互換性を保つために、OSVersionはWindows8のバージョンを返す仕様のようです。

マニフェストファイルでサポートOSを明示することで回避できます。

  <compatibility xmlns="urn:schemas-microsoft-com:compatibility.v1">
    <application>
      <!-- このアプリケーションがテストされ、協働するようテストされた Windows バージョンの
           一覧。適切な要素をコメント解除すると、最も互換性のある環境を Windows が
           自動的に選択します。-->

      <!-- Windows Vista -->
      <!--<supportedOS Id="{e2011457-1546-43c5-a5fe-008deee3d3f0}" />-->

      <!-- Windows 7 -->
      <!--<supportedOS Id="{35138b9a-5d96-4fbd-8e2d-a2440225f93a}" />-->

      <!-- Windows 8 -->
      <!--<supportedOS Id="{4a2f28e3-53b9-4441-ba9c-d69d4a4a6e38}" />-->

      <!-- Windows 8.1 -->
      <!--<supportedOS Id="{1f676c76-80e1-4239-95bb-83d0f6d0da78}" />-->

      <!-- Windows 10 -->
      <supportedOS Id="{8e0f7a12-bfb3-4fe8-b9a5-48fd50a15a9a}" />
    
    </application>
  </compatibility>

実行結果

Version Information:
   Major: 10
   Minor: 0

参考

C#逆引きレシピ

C#逆引きレシピ

C# 6.0 言語仕様 [非公式ドラフト版] を見つけた

C#

C# 5.0 (マイクロソフトが公開している最新)

C# Language Specification が 5.0 が公開されています。

Download C# Language Specification 5.0 from Official Microsoft Download Center

Visual Studio 2015から利用可能になった C# 6.0 言語仕様を探したところ、ドラフト版を公開している方(マイクロソフトの中の人)を発見しました。

正式版は、2016年年末までにマイクロソフトから発行される予定で、それまでの公開のようです。

WORD版とPDF版があります。PDF版を見ましたがコードにシンタックスハイライトもしてあって読みやすいです。

C# 6.0 言語仕様

PDF版

https://github.com/ljw1004/csharpspec/blob/gh-pages/CSharp%20Language%20Specification.pdf

リポジトリ

github.com

参考

Windows 10にアップグレードしたあと、HDMI オーディオが正常に動作しないためオーディオドライバーを更新した – ThinkPad T440s

下記を参考にオーディオドライバーを更新したところ、解決しました。

更新前

更新前のドライバーバージョンです。 f:id:cointoss1973:20160321182819p:plain

更新後

6.0.1.7522 にアップデートしました。 f:id:cointoss1973:20160321182823p:plain

無事TVに音声が出力されました。 これで拡張ディスプレイにしてニコ生をTV側に表示しながら作業することができますね。

参考

PowerShell で FizzBuzz

PowerShell

コマンドプロンプト(バッチファイル)より、PowerShell が充実していることは確かなので少しずつ使えるようになりたい。
そこでまずは FizzBuzz を実装してみた。

エディタや実行環境は、Visual Studio 2012 (Expressではない) に PowerShell Tools For Visual Studio をインストールした環境を準備*1

コードは以下*2

% で ForEach が書けます。$_ で要素がとれます。PowerShell 力が少し上がりました。


【改訂新版】 Windows PowerShell ポケットリファレンス

【改訂新版】 Windows PowerShell ポケットリファレンス

*1:2012 を使った理由はギョームで使っているからで深い理由はない。2013のほうがよいです。

*2:はてなブログは、PowerShellシンタックスハイライトに対応してないので、Gists経由で貼り付け