none
Tuple と IStructuralComparable について RRS feed

  • 質問

  • .NET 4 から、2 つの新しいインターフェイスが導入されました。

    IStructuralEquatable インターフェイス

    IStructuralComparable インターフェイス

    実装している代表的なクラスは、Array および Tuple です。

    これらの意味は、「オブジェクト自体(入れ物)の形(型)が違っても、その中身が同じならば、同じと判断する」ということだろうと理解しておりますが、これであっているでしょうか?

    例えば、以下のコードは

    var objArray = new object[] { 1, 2, 3 };
    var intArray = new int[] { 1, 2, 3 };
    
    Console.WriteLine(objArray.Equals(intArray)); // [1]
    Console.WriteLine(((IStructuralEquatable)objArray).Equals(intArray, EqualityComparer<int>.Default)); // [2]

    1 では False になりますが、2 では True になることから、自分の理解が裏付けられていると思っております。

    Array の場合、IStructuralComparable.CompareTo でも、そのような挙動を示します。

    var objArray = new object[] { 1, 2, 3 };
    var intArray = new int[] { 1, 2, 4 };
    
    Console.WriteLine(((IStructuralComparable)objArray).CompareTo(intArray, Comparer<int>.Default));
    
    // 結果は -1
    // つまり、{1, 2, 3} < {1, 2, 4}

    ところが、IStructuralComparable.CompareTo メソッド のドキュメントでは、異なる型を比較しようとしたときは ArgumentException がスローされると書かれています。

    Array はそんなことはなく、比較することができます。

    しかし、Tuple では例外が発生します。

    var objTuple = new Tuple<object, object, object>(1, 2, 3);
    var intTuple = new Tuple<int, int, int>(1, 2, 3);
    
    Console.WriteLine(((IStructuralComparable)objTuple).CompareTo(intTuple, Comparer<int>.Default));

    IStructuralEquatable.Equals でも、Tuple は違う型の場合は False を返します。

    もし、Tuple の実装が正しいのであれば、IStructuralComparable の理解は当初のようなものではなく、ただ、IComparer を指定することができるだけの IComparable ということになってしまいます。

    しかし、IStructuralComparable がジェネリック インターフェイスでないことや、StructuralComparisons.StructuralComparer の実装(要素が IStructuralComparable ならば、再帰的に比較する)を見るに、当初の理解が正しいと思うしかありません。

    異なる型の比較ができることこそ、IStructuralComparable の真価であると思われるところ、ドキュメントの記述と、Tuple の実装は不可解なものです。

    これはどう理解すればよいのでしょうか?

    よろしくお願いいたします。


    αετος(aetos)


    2012年5月17日 4:27

すべての返信

  • なにやら、ぼんやりと答えが見えてきました…。

    IStructuralComparable を実装する意図が、Array と Tuple では異なるのかもしれません。

    Array は既に判明している通り、コンテナの型にかかわらず、中身を比較するため(あるいは、Tuple と同じような意図もあるのかもしれません)。

    Tuple は、8 要素以上の場合は再帰的になることから、StructuralComparisons.Structural(Equality)Comparer を用いて、再帰的に比較するため。

    IStructuralComparable.CompareTo は要素を比較しますが、その引数は object です。

    これがタイプセーフでないのは、コンテナ自体の型が違うことを想定しているからです。

    Array は、比較相手が Array であることさえわかれば、GetValue メソッドによって値を取得できますが、Tuple にはそのような手段がなく、要素にアクセスするためには、Tuple 自体の型を(型引数まで含めて)決定しなければなりません。

    そのため、未知のコンテナ型とは、中身の比較ができないのですね。

    こうしてみると、タイプセーフも善し悪しだな…と思ってしまいますね。


    αετος(aetos)

    2012年5月17日 5:11
  • CompareTo メソッド のドキュメントでは、異なる型を比較しようとしたときは ArgumentException がスローされると書かれています。

    Array はそんなことはなく、比較することができます。

    しかし、Tuple では例外が発生します。

    屁理屈っぽくなりますが、T[]ではなくArrayクラスとしては同じ型ってことでしょうかね。
    2012年5月17日 6:17
  • 屁理屈っぽくなりますが、T[]ではなくArrayクラスとしては同じ型ってことでしょうかね。

    なるほど。確かに屁理屈っぽいですがw その通りと言えばその通りですね。

    しかし、じゃあどうして IStructuralComparable<T> じゃないんだろう…


    αετος(aetos)

    2012年5月17日 7:26