C# IComparerインターフェイス

C#において、配列やリストの並び替えなどで使うIComparerインターフェイスについて、実装時につまづいたことを挙げておきます。

例えば、次のような実装を行ったとき、

  public class CardCompare : IComparer<Card>
  {
    public int Compare (Card a, Card b)
    {
      int ret;

      if (a.Power < b.Power) {
        ret = -1; 
      } else {
        ret = 1;
      }
      return ret;
    }
  }

これは、List<Card>というリストのソート、Cardオブジェクト内のPowerによる並び替えを行うのが目的です。
仕様的にリスト内には同一のPowerのカードはないとすると、上記プログラムでret=0を戻す必要はないと思っていましたが、実は、ret=0の系が必要です。aとbにリスト内の同一オブジェクトが指定されて呼び出されることがあるからです。(稀なケースではなく、初期の並び次第では普通にありえます。)
ですので、

  public class CardCompare : IComparer<Card>
  {
    public int Compare (Card a, Card b)
    {
      int ret = 0;

      if (a.Power < b.Power) {
        ret = -1; 
      } else if(a.Power > b.Power){
        ret = 1;
      }
      return ret;
    }
  }

のように、aとbに同一オブジェクトが指定される系を考慮して、a.Powerとb.Powerが等しい場合はret=0になるようにする必要があります。
これをしておかないと、並びが意図しないものになることがあります。(「ことがある」、、、というのが厄介でした。)

念押しで言うと、
仕様的にリスト内には同一のオブジェクトがない場合でも、IComparerのCompareメソッドの引数には同一オブジェクトが指定されて呼び出されるケースの考慮が必須
ということです。

Android NDKにおけるシグナル発生時動作

Android NDKを使用している中で二点つまづいた点があったので、挙げておきます。

まず、AndroidのDalvik仮想マシンは、SIGQUITとSIGUSR1を使用するようで、これらシグナルのハンドリングを自分のプログラムで行うことはできません。
シグナルハンドラは呼び出されませんし、また、システムコールの中でEINTRを戻すような動作も行われません。ですので、使うのであれば、SIGUSR2あたりになるかと思います。(実は、SIGUSR2もDalvikのビルドスイッチ次第では使われるそうですが。)

二つ目は、usleepというシステムコールがシグナルによる割り込みが入っていも、EINTRで戻らないということです。理由は分かりませんがそのようになっています。
これには代案があります。それは、usleepの代わりにnanosleepを使用することです。nanosleepはシグナルによる割り込みに対して、EINTRで戻ってきます。

Unityでタイマー

Unityでプログラムを組んでいるなかで一つ気がつきました。
それは一般によくあるタイマー処理というものがないということです。つまり、発火する時間と発火したときに行う処理を設定するだけ、といったようなインタフェースがないようなのです。
調べてみると、ほとんどの解決策としてGameObjectのUpdateメソッドで毎回経過時間をカウントし、期待する時間になったときに処理を行うようにしている、とのことです。
これをヒントに複数のタイマを仕掛けられる汎用クラスを作成しました。スクリプトをシーン内のGameObjectにアタッチするだけで動作します。
タイマーのキャンセルなどはなく、必要最低限の処理にしています。利用者はSetメソッドで発火時の処理(onFinishDelay)と発火時間(delay)を設定するのみです。

using System.Collections;
using System.Collections.Generic;

public class MyTimer : MonoBehaviour
{
 
  public delegate void MyTimerDelegate ();
  
  public class MyTimerParam
  {
    MyTimer.MyTimerDelegate onFinishDelay;
    float delay;
    
    public MyTimer.MyTimerDelegate OnFinishDelay {
      set {
        this.onFinishDelay = value;
      }
      get {
        return this.onFinishDelay;
      }
    }
    
    public float Delay {
      set {
        this.delay = value;
      }
      get {
        return this.delay;
      }
    }
  }
  
  List<MyTimer.MyTimerParam> parameters;
  
  // Use this for initialization
  void Start ()
  {
    parameters = new List<MyTimer.MyTimerParam> ();  
  }
 
  // Update is called once per frame
  void Update ()
  {
    List<MyTimer.MyTimerParam> deleteParameters = new List<MyTimer.MyTimerParam> ();
    
    foreach (MyTimer.MyTimerParam param in parameters) {
      param.Delay -= Time.deltaTime;
      if (param.Delay <= 0) {
        param.OnFinishDelay ();
        deleteParameters.Add (param);
      }
    }

    foreach (MyTimer.MyTimerParam param in deleteParameters) {
      parameters.Remove (param);
    }
  }

  public MyTimer.MyTimerParam Set (MyTimer.MyTimerDelegate onFinishDelay, float delay)
  {
    MyTimer.MyTimerParam param = new MyTimer.MyTimerParam ();
    param.OnFinishDelay = onFinishDelay;
    param.Delay = delay;
    
    this.parameters.Add (param);
    
    return param;
  }

}