2016-03-22 82 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>.DefaultimplementationComparer<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

表現。每個對只有一次反射。 –

+0

啊,所以它隱式地緩存/記憶......這是我正在考慮添加的東西。 – RoadieRich