2016-03-22 6 views
1

これはthis questionの拡張であり、その特定のケースでうまくいきます。タイプ逆制約付きジェネリックの間の推論

私の実際のコードはより次のようになります。

public abstract class BaseComparable<TLeft, TRight> 
{ } 

public class LeftComparable<TLeft, TRight> : BaseComparable<TLeft, TRight> where TLeft : IComparable<TRight> 
{ 
    public LeftComparable(TLeft value) { } 
} 

public class RightComparable<TLeft, TRight> : BaseComparable<TLeft, TRight> where TRight : IComparable<TLeft> 
{ 
    public RightComparable(TLeft value) { } 
} 

あなたは私が掲示するものと同等の反射コードを使用する場合、それは素晴らしい作品:

public static BaseComparable<TLeft, TRight> AsComparableFor<TLeft, TRight>(this TLeft left, TRight right) 
{ 
    if (left is IComparable<TRight>) 
    { 
     var constructor = 
      typeof(LeftComparable<,>).MakeGenericType(typeof(TLeft), typeof(TRight)) 
             .GetConstructor(new[] { typeof(TLeft) }); 
     if (constructor != null) 
     { 
      return (BaseComparable<TLeft, TRight>)constructor.Invoke(new object[] { left }); 
     } 
    } 
    if (right is IComparable<TLeft>) 
    { 
     var constructor = 
      typeof(RightComparable<,>).MakeGenericType(typeof(TLeft), typeof(TRight)) 
             .GetConstructor(new[] { typeof(TLeft) }); 
     if (constructor != null) 
     { 
      return (BaseComparable<TLeft, TRight>)constructor.Invoke(new object[] { left }); 
     } 
    } 
    throw new ArgumentException(); 
} 

次にあなたが

class Baz 
{ 
    public int Value { get; set; } 
} 
class Bar : IComparable<Baz> 
{ 
    public int Value { get; set; } 
    int IComparable<Baz>.CompareTo(Baz other) 
    { 
     return Value.CompareTo(other.Value); 
    } 
} 

// .... 

var bar = new Bar { Value = 1 }; 
var baz = new Baz { Value = 1 }; 
var compBaz = baz.AsComparableFor(bar); 
var compBar = bar.AsComparableFor(baz); 
を言うことができます

ファンタスティックな型推論は、期待どおりに機能します。

上記受け入れ答えからの適応は、しかし、

public static class Comparable 
{ 
    public static BaseComparable<TLeft, TRight> 
        AsComparableFor<TLeft, TRight>(this IComparable<TRight> left, TRight right) 
    where TLeft : IComparable<TRight> 
    { 
     if (left is TLeft) 
     { 
      if (left is IComparable<TRight>) 
      { 
       return new LeftComparable<TLeft, TRight>((TLeft)left); 
      } 
     } 

     throw new InvalidCastException(); 
    } 

    public static BaseComparable<TLeft, TRight> 
        AsComparableFor<TLeft, TRight>(this TLeft left, IComparable<TLeft> right) 
    where TRight : IComparable<TLeft> 
    { 
     if (left is TLeft) 
     { 
      if (right is IComparable<TLeft>) 
      { 
       return new RightComparable<TLeft, TRight>((TLeft)left); 
      } 
     } 

     throw new InvalidCastException(); 
    } 
} 

は、明示的に型引数を述べるために、あなたが必要です:この

//bar.AsComparableFor(baz); 
//baz.AsComparableFor(bar); //Does not compile 

bar.AsComparableFor<Bar, Baz>(baz); 
baz.AsComparableFor<Baz, Bar>(bar); // Does compile 

大部分はとしてライブラリはよう無痛作ることでした可能であり、タイプを指定しなければならないと感じるのは幾分敗北します。

中間地はありますか?私は、元の型推論の強さと受け入れられた答えからクリーナー、反射のないコードを得ることができますか?

編集:full code can be found in this gist.

+0

比較するプロパティ( 'int Value')をコンパイル時に知ることができますが、どのクラスを比較するのが実行時に分かりますか?これは切断のようです。実行時に情報を挿入するのではなく、あらかじめ同等のクラスを構築できるようです。さらに、すべての型チェックを行うことは醜いです。おそらく、それは避けられないものですが、大変です。私はあなたがこれをさらに具体的にすることができたらいいと思う。異なるクラスを比較する必要性はなぜですか? – ErikE

+0

@ErikE '[Left | Right] ComparableとComparableはライブラリクラスです。 'Bar'と' Baz'は、ユーザーコードの例です。 – RoadieRich

答えて

1

私は、元の型推論の強さと受け入れ答えからクリーナー、無反射コードを入手することはできますか?

できません。実際には、値の種類のボクシングを含むため、受け入れられた答えは良くありません。

再び、反射を避けることはできません。

:何あなたがができることは唯一の違いは、代わりにシングルトンインスタンスを作成するのではなく、我々はシングルトン工場出荷時のデリゲートを作成しますということでしょう EqualityComparer<T>.Default implementationと同じ技術を用いて反射、 Comparer<T>.Defaultなどを最小限に抑える にあります
public abstract class BaseComparable<TLeft, TRight> 
{ 
    public static readonly Func<TLeft, BaseComparable<TLeft, TRight>> Factory = CreateFactory(); 
    private static Func<TLeft, BaseComparable<TLeft, TRight>> CreateFactory() 
    { 
     Type genericTypeDefinition; 
     if (typeof(IComparable<TRight>).IsAssignableFrom(typeof(TLeft))) 
      genericTypeDefinition = typeof(LeftComparable<,>); 
     else if (typeof(IComparable<TLeft>).IsAssignableFrom(typeof(TRight))) 
      genericTypeDefinition = typeof(RightComparable<,>); 
     else 
      throw new ArgumentException(); 
     var parameter = Expression.Parameter(typeof(TLeft), "value"); 
     var body = Expression.New(genericTypeDefinition 
      .MakeGenericType(typeof(TLeft), typeof(TRight)) 
      .GetConstructor(new[] { typeof(TLeft) }), parameter); 
     var lambda = Expression.Lambda<Func<TLeft, BaseComparable<TLeft, TRight>>>(body, parameter); 
     return lambda.Compile(); 
    } 
} 


public static class BaseComparable 
{ 
    public static BaseComparable<TLeft, TRight> AsComparableFor<TLeft, TRight>(this TLeft left, TRight right) 
    { 
     return BaseComparable<TLeft, TRight>.Factory(left); 
    } 
} 
+0

私のリフレクションバージョンよりもこの方法が好きな理由はありますか?「これはフレームワークの仕組みです」 - それ自体には理由が必要ですか? – RoadieRich

+0

パフォーマンス。これは、ペアごとに1回の反射のみです。 –

+0

ああ、それは暗黙のうちにキャッシング/メモです...それは私がとにかく追加を検討していたものでした。 – RoadieRich

関連する問題