C# 技術

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メソッドの引数には同一オブジェクトが指定されて呼び出されるケースの考慮が必須
ということです。

-C#, 技術
-

© 2021 BLuE AND PuRE