試験運用中なLinux備忘録・旧記事

はてなダイアリーで公開していた2007年5月-2015年3月の記事を保存しています。

Vala言語とC#言語でのプロパティ値変更時の通知について

PyGObjectで gobject.GObjectクラスを継承してGObjectプロパティを用いる(後半)」ではPyGObjectでプロパティ値が変更されたときにGObjectのシグナルを用いてハンドラと関連付ける処理を扱ったが、ここではVala言語とC#言語の場合についてを扱う。

Vala

Vala言語はGLib(GObjectライブラリ含む)を常に用いるので、標準の機能としてGObjectのプロパティが扱え、値の変更時のシグナルもPyGObjectと同様に使用可能で、特定のプロパティに対してのみシグナルを接続するということも可能となっている。

/* いずれかのプロパティ値が変更されたときにハンドラが呼ばれるようにする */
[GLib.Objectを継承したクラスのオブジェクト].notify.connect([ハンドラ]);

/* 特定のプロパティ値が変更されたときにハンドラが呼ばれるようにする */
[GLib.Objectを継承したクラスのオブジェクト].notify["[プロパティ名]"].connect([ハンドラ]);

更に、プロパティ定義時の記述によっては例外としてハンドラが呼ばれないようにすることもできる。

/* notifyシグナルを接続しても通知をしないプロパティ */
[CCode (notify = false)]
public [型] [プロパティ名] { ... }

下は例。

namespace PropertyNotificationTest
{
  /* プロパティ変更の通知はGLib.Objectから派生したクラスでのみ利用可能 */
  class PropertyNotificationTest : GLib.Object
  {
    public PropertyNotificationTest ()
    {
      /* いずれかのプロパティが変更されたときにハンドラが呼ばれるようにする */
      this.notify.connect ((property) =>
      {
        print ("property \"%s\" has been changed\n", property.name);
      });
      /* プロパティ名を指定して個別に接続することが可能 */
      this.notify["c"].connect (() =>
      {
        print ("PROPERTY \"c\" HAS BEEN CHANGED !!!\n");
      });
    }
    public int a { get; set; }
    public string b { get; set; }
    public double c { get; set; }
    /* notifyシグナルを接続しても通知をしないプロパティ */
    [CCode (notify = false)]
    public long d { get; set; }
  }

  class MainClass
  {
    public static void main (string[] args)
    {
      print (">>> PropertyNotificationTest obj = new PropertyNotificationTest ();\n");
      PropertyNotificationTest obj = new PropertyNotificationTest ();
      print (">>> obj.a = 1;\n");
      obj.a = 1;
      print (">>> obj.b = \"abc\";\n");
      obj.b = "abc";
      print (">>> obj.a:%d obj.b:%s \n\n", obj.a, obj.b);

      print (">>> obj.a = 2;\n");
      obj.a = 2;
      print (">>> obj.b = \"zzz\";\n");
      obj.b = "zzz";
      print (">>> obj.a:%d obj.b:%s \n\n", obj.a, obj.b);

      print (">>> obj.a = 3;\n");
      obj.a = 3;
      print (">>> obj.b = \"test\";\n");
      obj.b = "test";
      print (">>> obj.a:%d obj.b:%s \n\n", obj.a, obj.b);

      print (">>> obj.c = 1.1;\n");
      obj.c = 1.1;
      print (">>> obj.c:%f\n\n", obj.c);

      print (">>> obj.d = 12345;\n");
      obj.d = 12345;
      print (">>> obj.d:%ld\n", obj.d);
    }
  }
}

下は実行結果。

>>> PropertyNotificationTest obj = new PropertyNotificationTest ();
>>> obj.a = 1;
property "a" has been changed
>>> obj.b = "abc";
property "b" has been changed
>>> obj.a:1 obj.b:abc 

>>> obj.a = 2;
property "a" has been changed
>>> obj.b = "zzz";
property "b" has been changed
>>> obj.a:2 obj.b:zzz 

>>> obj.a = 3;
property "a" has been changed
>>> obj.b = "test";
property "b" has been changed
>>> obj.a:3 obj.b:test 

>>> obj.c = 1.1;
property "c" has been changed
PROPERTY "c" HAS BEEN CHANGED !!!
>>> obj.c:1.100000

>>> obj.d = 12345;
>>> obj.d:12345

C#

C#では

  • クラスはSystem.ComponentModel.INotifyPropertyChangedインターフェースを継承したものでないといけない
  • PropertyChangedイベントを定義し、更にそのイベントを発生させるためのメンバ関数を用意する
  • setアクセサ(setter)の中でそのメンバ関数を呼ぶように記述する
  • PropertyChangedイベントのハンドラを記述し、「+=」で関連付ける

といった面倒な手順を踏む必要があり、プロパティの定義もこの関係で「{ get; set; }」のように簡単に記述することはできない。
この形自体は決まっているが、クラスごとに毎回記述する必要があり、その上、特定のプロパティだけに対して変更を通知するようにはできない。
下は例。

using System.ComponentModel;
using System;

namespace PropertyNotificationTest
{
  class PropertyNotificationTest : INotifyPropertyChanged
  {
    public PropertyNotificationTest ()
    {
      this.PropertyChanged += (sender, e) =>
      {
        Console.WriteLine ("property \"{0}\" has been changed", e.PropertyName);
      };
    }
    public event PropertyChangedEventHandler PropertyChanged;
    public void NotifyPropertyChanged (string propname)
    {
      if (this.PropertyChanged != null)
      {
        this.PropertyChanged (this, new PropertyChangedEventArgs (propname));
      }
    }
    int __a;
    string __b;
    public int a
    {
      get
      {
        return this.__a;
      }
      set
      {
        this.__a = value;
        this.NotifyPropertyChanged ("a");
      }
    }
    public string b
    {
      get
      {
        return this.__b;
      }
      set
      {
        this.__b = value;
        this.NotifyPropertyChanged ("b");
      }
    }
  }
  class MainClass
  {
    public static void Main (string[] args)
    {
      Console.WriteLine (">>> PropertyNotificationTest obj = new PropertyNotificationTest ();");
      PropertyNotificationTest obj = new PropertyNotificationTest ();
      Console.WriteLine (">>> obj.a = 1;");
      obj.a = 1;
      Console.WriteLine (">>> obj.b = \"abc\";");
      obj.b = "abc";
      Console.WriteLine (">>> obj.a:{0} obj.b:{1} \n", obj.a, obj.b);

      Console.WriteLine (">>> obj.a = 2;");
      obj.a = 2;
      Console.WriteLine (">>> obj.b = \"zzz\";");
      obj.b = "zzz";
      Console.WriteLine (">>> obj.a:{0} obj.b:{1} \n", obj.a, obj.b);

      Console.WriteLine (">>> obj.a = 3;");
      obj.a = 3;
      Console.WriteLine (">>> obj.b = \"test\";");
      obj.b = "test";
      Console.WriteLine (">>> obj.a:{0} obj.b:{1}", obj.a, obj.b);
    }
  }
}

下は実行結果。

>>> PropertyNotificationTest obj = new PropertyNotificationTest ();
>>> obj.a = 1;
property "a" has been changed
>>> obj.b = "abc";
property "b" has been changed
>>> obj.a:1 obj.b:abc 

>>> obj.a = 2;
property "a" has been changed
>>> obj.b = "zzz";
property "b" has been changed
>>> obj.a:2 obj.b:zzz 

>>> obj.a = 3;
property "a" has been changed
>>> obj.b = "test";
property "b" has been changed
>>> obj.a:3 obj.b:test

関連記事:

参考URL:

使用したバージョン:

  • Vala 0.7.10
  • Mono 2.4.2.3