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

銀の弾丸

プログラミングに関して、いろいろ書き残していければと思っております。

最近気づいたVisualStudio 2015 C# で便利に使える5つの機能

全国的に梅雨も明け、本格的な夏ですね。 自分的には(仕事で)年に一度の恒例のVisualStudioシーズン・イン。 昨年までは冬場が多く、期間は長くても2か月程度。 しかし今年は 6月初めから徐々に動き出して、7月以降に本格化。 期間はトータル4、5か月になりそうです。 終わるころには秋ですね。

例年そんなインターバルでやっているので、VisualStudioやC#の、最新開発状況(環境やプログラミングスタイルなど)になかなか追従できていないのですが、今回は多少期間が長いため、視野がちょっとだけ広くなったか「あ、こんなことができるんだあ」とか「こんな風に書けるのねー」みたいなことが何度かありましたので書いておきます。

あくまでも、自分が知らなくて最近気が付いたってことですので、皆さんご存知なことばかりかも。 しかし、いくつになってもお勉強。新知識には興奮します。

f:id:takamints:20160730125037p:plain

メニュー

  1. プロパティ名を指定せずOnPropertyChangedを呼ぶ
  2. nullチェックの簡略記法
  3. 自動実装プロパティの初期値設定
  4. 読み出し専用プロパティを自動実装
  5. プロパティやメソッドのスニペットを挿入する

1. プロパティ名を指定せずOnPropertyChangedを呼ぶ

INotifyPropertyChangedを実装したクラスのプロパティに値を設定されたとき、PropertyChangedイベントを発生させますが、これまで以下のようにプロパティ名を文字列で指定して、OnPropertyChangedメソッドを呼び出していました。律儀にね。

using System.ComponentModel;
namespace Application {
  class ViewModel: INotifyPropertyChanged {
    public event PropertyChangedEventHandler PropertyChanged;
    public virtual void OnPropertyChanged(string name) {
      if(PropertyChanged != null) {
        PropertyChanged.Invoke(this, new PropertyChangedEventArgs(name));
      }
    }
    private int _propFoo;
    public int PropFoo {
      get { return _propFoo; }
      set {
        _propFoo = value;
        OnPropertyChanged("PropFoo");
      }
    }
  }
}

ところが、プロパティが増えると、そのうち必ず間違えるでしょう?それに、名前を変えたら文字列も変えなきゃならないわけで。 多分おそらく、やってられないですよコレは。

そこで、

[CallerMemberName]を使いましょう

Call Me Maybe
Call Me Maybe
posted with amazlet at 16.07.30
Universal Music LLC (2014-02-03)
売り上げランキング: 135

以下のように、OnPropertyChangedメソッドの、プロパティ名称を受け取る文字列引数を、省略可能(既定値は空文字列)にして、[System.Runtime.CompilerServices.CallerMemberName]属性を設定しておくと、引数を省略して呼び出しても、呼び出し元のプロパティ名(以下の例では"PropFoo")が自動的に渡されるのです。

using System.ComponentModel;

namespace Application {
  class ViewModel: INotifyPropertyChanged {
    public event PropertyChangedEventHandler PropertyChanged;

    // CallerMemberNameアトリビュートを名前を受け取る文字列引数に指定する。
    public virtual void OnPropertyChanged(
      [System.Runtime.CompilerServices.CallerMemberName]
      string name = "")
    {
      if(PropertyChanged != null) {
        PropertyChanged.Invoke(this, new PropertyChangedEventArgs(name));
      }
    }
    private int _propFoo;
    public int PropFoo {
      get { return _propFoo; }
      set {
        _propFoo = value;

        //引数を省略して呼び出すと、このプロパティ名"PropFoo"が渡される
        OnPropertyChanged();
      }
    }
  }
}

これなら、リファクタリングし放題ですねっ。

※ 上の例では、あえて冗長に属性名をフルパスで書いていますが、実際の局面では、いきなり[CallerMemberName]と書いてから(この時点ではコンパイルエラーかも)、[Ctrl]+[.]で、using System.Runtime.CompilerServicesを追加すれば良いですよ。

つまり呼び出し元のメンバ名が渡される

ここの例では、プロパティから呼び出しているのでプロパティ名になっていますが、メソッドから呼び出せば、そのメソッド名が渡されます。 CallerMemberNameの名のとおり、呼び出した側のメンバ名が渡される。

本来イベントとは何の関係もない機能ですが、気付くきっかけが「OnPropertyChangedじゃまくせー」と思って調べていた時だったのでこうなった。 デバッグログを出力するような場合にも有用でしょうね。

2. nullチェックの簡略記法

if文でオブジェクトがnullでないことを確認してから、そのメソッドを呼び出す処理は、さらにシンプルに書けます。 一つ上の例では、イベントのリスナーがいるかどうかを確認しています(以下に抜き出してます)

    public event PropertyChangedEventHandler PropertyChanged;
    public virtual void OnPropertyChanged(string name) {
      if(PropertyChanged != null) {//←ここ
        PropertyChanged.Invoke(this, new PropertyChangedEventArgs(name));
      }
    }

長年「こういうものだ」と思っていたので特に不便さは感じていませんでしたが、最近のC#では、以下のように?を使って短く書ける。知ってしまうともう戻れない。

    public event PropertyChangedEventHandler PropertyChanged;
    public virtual void OnPropertyChanged(string name)
    {
      PropertyChanged?.Invoke(this, new PropertyChangedEventArgs(name));
    }

対象のオブジェクト(上の例ではPropertyChanged)がnullでないならそのままメソッドを呼び出しますが、nullなら何も行わない。

最初、「え?」と思いました。そういえばnull許容型で似た記述をしますが、三項演算子の発展形(省略形?)のようにも思えます。

これは、メソッド呼び出しのための構文ではなく対象オブジェクトがnullかどうかによって、その後のメンバーの参照をするかどうかということです。 そして、以下のようにチェーンできますから、オブジェクトの階層が深い場合は、かなり有利。途中のプロパティやメソッドの戻り値がnullなら、nullとして評価され、それ以降は評価されないということですね(多分)。

  Foo?.Bar?.Baz()?.Hoge("Fuga");

メッチャ強力。過去のコードを修正したくなりますね(しないけど)。いやしかし、こりゃ楽でいい。

3. 自動実装プロパティの初期値設定

地味ではあるけど、これも由。

自動実装プロパティに特定の初期値を与えるためには、以下のように、コンストラクタで値を設定しなくてはならなかったと思っていましたが、

//従来の自動実装プロパティを持つクラス
class Foo {
  public int Bar { get; set; }
  public Foo() {
    Bar = 999;
  }
}

プロパティ宣言部分で値を設定できる(以下)ようになっていました。

//最新式(笑)の自動実装プロパティを持つクラス
class Foo {
  public int Bar { get; set; } = 999;
}

これも楽です便利です。

自動実装で楽しているにもかかわらず、コンストラクタを別途定義するとかアホちゃうかと潜在的に思っていましたが、やっと報われた(謎)。

4. 読み出し専用プロパティを自動実装

もうひとつ自動実装プロパティネタ。

GetterがパブリックでSetterはプライベートというプロパティは自動実装できないものだと思い込んでいて、常々以下のように書いていました。

class Foo {
  private int _bar = 999;
  public int Bar { get { return _bar;} }
}

でも、以下のように書けるのだとか。

class Foo {
  public int Bar { get; private set; } = 999;
}

これは、自分が知らなかっただけかな。

5. 自動実装プロパティのスニペットを挿入する

プロパティを新設するとき、全部自分でキー入力していましたが、エディタでpropと入力して、[Tab]を2回叩けば、とりあえずint型のMyPropertyという自動実装プロパティが挿入されますね。

ほかにも便利なスニペットがあるかもしれん。いろいろ探し歩いてみます。