2016-03-22 81 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