オーバーライドされたプロパティ

Posted 2013.05.13 in 吉里吉里

派生クラスによってオーバーライドされて隠蔽されてしまい
普通にアクセスできなくなった基底クラスの関数にアクセスするには
incontextof 演算子を使って


(global.Layer.update incontextof layer)()

と書けばよいが、オーバーライドされたプロパティにアクセスしようと同じノリで


(global.Layer.left incontextof layer)

と書いてもこれはエラーになる。
incontextof 演算子の意味を考えればあたり前なのだけれど…
ちょっとハマりがちなところだと思う。

オーバーライドされたプロパティにアクセスするには、次の3つの方法がある。

1. superを使う


var value = super.left;

superキーワードを介して親クラスのプロパティにアクセスできる。

この方法は最も単純明快だが、この方法を使えるのはそのクラス内だけである。
super を使って外からアクセスできるようにするには別名のプロパティを作って
そこから super を介してアクセスするといったような回りくどい方法をとる必要がある。

2. プロパティオブジェクトを使う


var value = *(&global.Layer.left incontextof layer);

親クラスのオブジェクトから&演算子でプロパティオブジェクトを取得し、
incontextof 演算子でコンテキストを置き換えて*演算子で参照する方法。
なんだか難しそうな表記だが、&演算子と*演算子の意味を理解すれば単純だ。

&演算子で取得したプロパティオブジェクトは、変数に格納して使うこともできる。


var propLeft = (&global.Layer.left incontextof layer);
var value = *propLeft;

3. クラスオブジェクトを使う


var value = (global.Layer incontextof layer).left;

親クラスのオブジェクトのコンテキストを置き換えて使う方法。
この方法はたぶんあまり知られていない気がする。
プロパティオブジェクトを使うよりこっちの方が直感的だろう。

変数に格納して使うこともできるので、
たくさんのオーバーライドされたプロパティにアクセスする場合には特に便利だ。


var globalLayer = (global.Layer incontextof layer);
var value = globalLayer.left;

速度の比較

3つのアクセス方法の速度を計測してみよう。


class MyLayer extends Layer
{
  function MyLayer(window, parent)
  {
    super.Layer(...);
    super.left = 222;
  }

  var m_left = 111;
  property left
  {
    setter(v){ m_left = v; }
    getter(){ return m_left; }
  }

  property superLeft
  {
    getter(){ return super.left; }
  }
}

class TestWindow extends Window
{
  function TestWindow()
  {
    super.Window();
    add(new Layer(this, null));
  }

  function test()
  {
    var TEST_NUM = 10000000;
    var layer = new MyLayer(this, primaryLayer);
    var value;
    {
      var begin = System.getTickCount();
      for(var i=0; i < TEST_NUM; i++)
        value = layer.superLeft;
      var end = System.getTickCount();
      Debug.message("value = layer.superLeft : "+(end-begin)+" ms");
    }
    {
      var begin = System.getTickCount();
      for(var i=0; i < TEST_NUM; i++)
        value = *(&global.Layer.left incontextof layer);
      var end = System.getTickCount();
      Debug.message("value = *(&global.Layer.left incontextof layer) : "
        +(end-begin)+" ms");
    }
    {
      var begin = System.getTickCount();
      for(var i=0; i < TEST_NUM; i++)
        value = (global.Layer incontextof layer).left;
      var end = System.getTickCount();
      Debug.message("value = (global.Layer incontextof layer).left : "
        +(end-begin)+" ms");
    }
    Debug.message("------");
    {
      var begin = System.getTickCount();
      var propLeft;
      for(var i=0; i < TEST_NUM; i++)
        propLeft = (&global.Layer.left incontextof layer);
      var end = System.getTickCount();
      Debug.message("propLeft = (&global.Layer.left incontextof layer) : "
        +(end-begin)+" ms");
    }
    {
      var begin = System.getTickCount();
      var propLeft = (&global.Layer.left incontextof layer);
      for(var i=0; i < TEST_NUM; i++)
        value = *propLeft;
      var end = System.getTickCount();
      Debug.message("value = *propLeft : "+(end-begin)+" ms");
    }
    {
      var begin = System.getTickCount();
      var globalLayer;
      for(var i=0; i < TEST_NUM; i++)
        globalLayer = (global.Layer incontextof layer);
      var end = System.getTickCount();
      Debug.message("globalLayer = (global.Layer incontextof layer) : "
        +(end-begin)+" ms");
    }
    {
      var begin = System.getTickCount();
      var globalLayer = (global.Layer incontextof layer);
      for(var i=0; i < TEST_NUM; i++)
        value = globalLayer.left;
      var end = System.getTickCount();
      Debug.message("value = globalLayer.left : "+(end-begin)+" ms");
    }
    invalidate layer;
  }
} 

var win = new TestWindow();
win.visible = true;
Debug.console.visible = true;
win.test();

実行結果。


value = layer.superLeft : 4892 ms
value = *(&global.Layer.left incontextof layer) : 2440 ms
value = (global.Layer incontextof layer).left : 2728 ms
------
propLeft = (&global.Layer.left incontextof layer) : 2414 ms
value = *propLeft : 414 ms
globalLayer = (global.Layer incontextof layer) : 1895 ms
value = globalLayer.left : 862 ms

下の4つはコンテキストを置き換える部分の速度と、
コンテキストを置き換えたオブジェクトを変数に格納してアクセスした場合の速度だ。

計測結果から次のことがわかる。

・incontextof 演算子は単にオブジェクトのコンテキストを置き換えるだけにすぎないので非常に高速
・別名のプロパティを介してsuperキーワードを使うよりは、incontextof 演算子を使う方がずっと速い
・一度コンテキストを置き換えてしまえば、さらに高速にアクセスできる
・プロパティオブジェクトを使う方法が最速
・複数のプロパティにアクセスする場合は、クラスオブジェクトを使う方法が効率的

とはいえこの速度差は大抵の場合問題にならないだろう。

まとめ

オーバーライドされたプロパティにアクセスする方法には、どの方法にも一長一短がある。
状況に合わせてうまく使い分けていきたい。


Leave a Reply

*