de:code2018 に行ってきた

技術や情報、ヒトとモノ、行きかう出会う触る知る、「お土産」は物質だけじゃないから、この手のイベントはやめられないw あ、会場を着物でウロウロしていたのが私ですw

Microsoftさんとこのイベント、de:code2018 今年もアプリ提供で色々と捗りました。

https://www.microsoft.com/ja-jp/events/decode/2018/

Screenshot_20180524-184756Screenshot_20180524-184814

行ったセッションはこんな感じ。

アプリケーション開発(デスクトップ&.NET)を中心にして、興味ある分野(CSIRTとか、オンプレとか)つまみ食い。

体調と相談しながらだったからフル参戦はできなかったorz

今はチマチマと自己満足ライブラリ作ってたり、ときどきコンサルしたり、翻訳したり、だから、べったり開発者です的な活動はしていないけれど、こういったイベントで「何か」を得ること、それは知識や出会いや再会や10%引きの関連書籍やノベルティとかかもしれないけど、私の言葉でいう「お土産」をもって帰ること、はとても大事っていつも思う。

セッションの終了には「Ask the speaker」ってのがあって、セッションしてくれた人と一対一で話せるし、DAY1の最後にはパーティがあって、偶然同じテーブルになって一緒に乾杯して大盛り上がりしちゃうかもしれない。袖すりあうも、で、名前きいたら、知ってるコミュニティの人だったり、とある書籍の作者だったりするかもしれない、掲示板で質問に答えてくれていた人かもしれない、それでいいと思うんだ。市場調査、知識吸収、それぞれの目的があるし、その目的のためにイベント参加もあると判る一方で、きっとそれ以上の「お土産」があるのが「イベント」であり、「勉強会」だと私は思う。その「お土産」に自分の資産(時間とお金)をどこまで使えるのか、最後はそこに尽きるけどw

システム屋って、案外、孤独な時ってあるんよね。私みたいに、おひとり様情シスとか、Sierもどきとか、いろんな業種業界業態で、時には全く異世界な環境でITやってたりとかしてるとね、そんな気持ちをもったりすることあるんだ。そんなとき、そんな大変さを知っている人たちと知り合いでいる、ってことはとても支えになったりすることある。大変さを知らなくても「大変な人もいるんだ」と生で知ることも、きっと、そうならないための試金石になれるし、反面教師にもなれる。私としては、「同じ轍は踏んでほしくない」っていつも思うし。だから、「お土産」を何か持ち帰ってほしいなぁと、その「お土産」に私がちょっとでも手伝えていたらいいなぁ、と思ったりもする。こういうところに現れる時にはねv

といっても、アプリにはまだまだ改善点があるし、会場動線の問題、休憩できる場所が少ない&わかりづらい、とかあるけれど、

んなことはとりあえず置いといて、

今回も、たくさんの「お土産」をありがとう。

キーボードのNumLock状態をタスクトレイで確認できるようにした

えー、フツーでしょ、My.Computerクラスとか、え? 使えないの? インスタンス取るの? メンドクセー!!! が第一声w

新しい作業用PC、画面大きいし、テンキーあるし、メモリあるし、今のところ不満――あるんだな、これが。

NumLockキーのインジケーターが存在しないw

つまり、NumLockキーON/OFFが目視できないんだわ、いや、あるの当たり前だと思ってたんだけどさ、なかったんだわ、このPC。

なので、タスクトレイにインジケータつけておけばよくね?という発想に至る。

まぁ、あれだ、VBだと、さくっと一行で解決するんだわ。

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

My.Computer.Keyboardにずばり、NumLock のBooleanいるしね。さくっと書いて終わり。

んじゃ面白くないから、勉強かねてC#でやろうとしたらさ、そしたらさ、My.Computer がない。My空間そのものがない。

まじかーorz

しょうがないから、Microsoft.VisualBasic.Devices 参照追加して、自分で書くorz

んでもって、作ったのがこちら。長いよw

/// <summary>
/// タスクトレイ専用アイコン制御するフォームクラス
/// </summary>
internal class MainForm : Form
{
    //動的変更するのでコンテナ化しとく
    private IContainer components = null;

    //アイコンも固定で持つ
    private NotifyIcon icon;

    //終わるよメニューは固定でもつ
    private ContextMenuStrip menu;

    private ToolStripMenuItem menuItem;

    //VBer必須のMy.Computerクラス
    private Computer myComputer;

    //タイマー
    private Timer timer;

    public MainForm()
    {
        //一回だけ処理
        ShowInTaskbar = false;
        initialize();
    }

    private void Close_Click(object sender, EventArgs e)
    {
        // 終わるよイベント 
        timer.Stop();
        timer.Dispose();
        Application.Exit();
    }

    private void initialize()
    {
        //初期化処理
        myComputer = new Computer();
        components = new System.ComponentModel.Container();
        icon = new NotifyIcon(components);

        //メニュー構成
        setMenu();

        //通知アイコン設定
        setNotifyIcon();

        //タイマー初期化
        timer = new Timer();
        timer.Interval = 1000;
        timer.Tick += new EventHandler(intervalCheck);
        timer.Start();
    }

    private void intervalCheck(object sender, EventArgs e)
    {
        // タイマーイベント 
        timer.Stop();
        setNotifyIcon();
        Refresh();
        timer.Start();
    }

    private void setMenu()
    {
        //メニュー作成
        menu = new ContextMenuStrip();
        menuItem = new ToolStripMenuItem();

        // 簡単な単語の場合、リソース提供のテキストにしておくと、
        // あとでGlobal化するとき困らないw
        menuItem.Text = Properties.Resources.M0001;
        //イベントハンドラ登録
        menuItem.Click += new EventHandler(Close_Click);

        menu.Items.Add(menuItem);
    }

    private void setNotifyIcon()
    {
        //VBerなら当たり前のことをC#でもさせてみるw
        //まさか、自分でクラスインスタンスとらないと
        //My.Computer使えないとかびっくりしたよ
        if (myComputer.Keyboard.NumLock)
        {
            icon.Icon = Properties.Resources.On;
            icon.Text = "NumLock ON";
        }
        else
        {
            icon.Icon = Properties.Resources.Off;
            icon.Text = "NumLock OFF";
        }
        icon.ContextMenuStrip = menu;
        icon.Visible = true;
    }
}

/// <summary>
/// NumLockすぐにわかるよインジケータ
/// </summary>
internal class MainWindow
{
    private static void Main()
    {
        MainForm rm = new MainForm();
        Application.Run();
    }
}

実行結果がこちら

image

あとはスタートアップに突っ込んでしばらく使っていろいろとチューニングしてくw

NUnit 3.x で Private メソッドをテストできるようにした

PrivateObjectクラスってすげー。MSTestって地味に頑張ってるんだなぁと思った。

やりたかったこと。とあるクラスのPrivateメソッドをテストしたかった。

MSTestの場合、PrivateObjectという大変使える?クラスがいたので、それを使ってテスト対象クラスインスタンスからInvokeすれば解決してたんだよね。

https://msdn.microsoft.com/ja-jp/library/microsoft.visualstudio.testtools.unittesting.privateobject.aspx

まぁ、これに同等の機能クラスが実はNUnit3.xに存在しないってことを知ってしまったわけだ。

なので、作ったw

internal static class CommonModule
{
  static public Object PrivateMethodInvoke(Object testClass, String methodName, Object[] parameters)
  {
    if (string.IsNullOrWhiteSpace(methodName))
        Assert.Fail("No Name");

    MethodInfo method = testClass.GetType().GetMethod(methodName, BindingFlags.NonPublic | BindingFlags.Instance);

    if (method == null)
        Assert.Fail(string.Format("{0} is not found", methodName));

    try
    {
        return method.Invoke(testClass, parameters);
    }
    catch (TargetInvocationException ex)
    {
        throw ex.InnerException;
    }
  }
}

例外についても今まで通りの形でテストしたかったんで、ちょいテク。Try-Catch でInvoke例外とってその原因の例外を実行時例外に代替。ILコスト? まーいいんじゃない? テストコードだし、実行じゃないしw

で、テスト用にサンプルクラス作る。Privateメソッドでパラメータの文字列を数字にして「倍返し」。

public class ForPrivate
{
  private int TwoTimeString(String BaseNum)
  {
    if (string.IsNullOrWhiteSpace(BaseNum))
        throw new InvalidCastException();

    int ret = int.MinValue;

    if (int.TryParse(BaseNum, out ret))
    {
        return ret * 2;
    }

    throw new InvalidCastException();
  }
}

そして、テストコードを書いてみる。正常ケースと例外ケースを。

public class ForPrivateTest
{
  [Test, Sequential]
  public void TwoTimeStringTest([Values("1", "4", "88", "-4", "0")] String prm1,
                                [Values(2, 8, 176, -8, 0)] int Exp1)
  {
    ForPrivate forPrivate = new ForPrivate();
    Assert.NotNull(forPrivate);

    int act = (int)CommonModule.PrivateMethodInvoke(forPrivate, "TwoTimeString", new object[] { prm1 });
    Assert.AreEqual(Exp1, act);
  }

  [Test, Sequential]
  public void TwoTimeStringExceptionTest([Values("", " ", "0.00004", null)] String prm1)
  {
    ForPrivate forPrivate = new ForPrivate();
    Assert.Throws<InvalidCastException>(() => CommonModule.PrivateMethodInvoke(forPrivate, "TwoTimeString", new object[] { prm1 }));
  }
}

実行してみた。

image

グリーンだよv

C#でEnum型用の拡張メソッドを作ってみた

JavaだとEnumの中にかけるから、基底Enum作って継承すれば良いんだけどさ。不便よね、もうw

C#やVBでのEnum型って所詮は Int だらけの構造体w<やめい

なので、汎用的にごにょりたいときは拡張メソッド使うw

使うEnum型はこういうの。

public enum ColorDefine
{
    [Description("不明")]
    None = 0,

    [Description("緑")]
    Green = 1,

    [Description("赤")]
    Red = 2,

    [Description("青")]
    Blue = 3,

    [Description("黄")]
    Yellow = 4
}

このEnumに日本語名称をつけて、これを拡張メソッドで取れるようにする。

public static string GetProperName<T>(this T Value) 
{
    if (!(typeof(T).IsEnum))
    {
        throw new InvalidEnumArgumentException();
    }

    FieldInfo fieldInfo = Value.GetType().GetField(Value.ToString());
    if (fieldInfo == null) return null;

    var attr = (DescriptionAttribute)fieldInfo.GetCustomAttribute(typeof(DescriptionAttribute));
    if (attr == null) return "";
    return attr.Description;
}

といいつつ、この拡張メソッドはすべての型に適用可能になっててインテリセンスにも出てくる。使っちゃうと例外かっとぶのでそれはそれで問題。なのでEnum型に絞ることを考えてみる。

まぁ、Whereで制約をつければ良いんだけどね。

https://docs.microsoft.com/ja-jp/dotnet/csharp/programming-guide/generics/constraints-on-type-parameters

image

じゃぁ、Enum型にはどの制約をつければいいのか、って時に、Docs大事。

https://docs.microsoft.com/ja-jp/dotnet/api/system.enum?view=netstandard-2.0

image

この記載をみれば、Where定義に何を書けばいいのかが判る。

ちな、ValueTypeはWhere定義では叱られるけど、ここは値型のstructでOK。だって所詮はInt型の(ry。というわけでリファクタリング。

public static string GetProperName<T>(this T Value) 
    where T : struct, IComparable, IConvertible, IFormattable
{
    if (!(typeof(T).IsEnum))
    {
        throw new InvalidEnumArgumentException();
    }

    FieldInfo fieldInfo = Value.GetType().GetField(Value.ToString());
    if (fieldInfo == null) return null;

    var attr = (DescriptionAttribute)fieldInfo.GetCustomAttribute(typeof(DescriptionAttribute));
    if (attr == null) return "";
    return attr.Description;
}

制約入れたのに、まだEnum型かどうかのチェックをしているのは、Enum型が所詮は Int の列挙型にすぎないので、この制約でもInt型変数に対してメソッドが使用可能となるから。なのでそれもあわせてテストに書いて

[Test, Sequential]
public void GetProperNameColorDefine([Values]ColorDefine prm1,
                          [Values("不明", "緑", "赤","青","黄")] string exp1)
{
    var act = Extenstions.GetProperName(prm1);
    Assert.AreEqual( exp1, act);
}
[Test]
public void GetProperNameColorDefineException()
{
    int Err = 200;
    Assert.Throws<InvalidEnumArgumentException>(() => Err.GetProperName());
}

実行したら

image

グリーンだよv