与反向约束的仿制药之间的类型推断 [英] Type Inference between generics with reversed constraints
问题描述
这是从这个问题,其中有在特定情况下,工作的一个答案的扩展。
我实际的代码看起来更像是这样的:
公共抽象类BaseComparable< TLeft, TRight>
{}
公共类LeftComparable< TLeft,TRight> :BaseComparable< TLeft,TRight>其中,TLeft:IComparable的< TRight>
{
公共LeftComparable(TLeft值){}
}
公共类RightComparable< TLeft,TRight> :BaseComparable< TLeft,TRight>其中,TRight:IComparable的< TLeft>
{
公共RightComparable(TLeft值){}
}
如果您使用等效反射代码什么我张贴,它的伟大工程:
公共静态BaseComparable< TLeft,TRight> AsComparableFor< TLeft,TRight>(这TLeft左,TRight右)
{
如果(左边是IComparable的< TRight>)
{
变种构造=
typeof运算( LeftComparable<,方式>)MakeGenericType(typeof运算(TLeft)的typeof(TRight))
.GetConstructor(新[] {typeof运算(TLeft)});
如果(构造!= NULL)
{
回报率(BaseComparable< TLeft,TRight>)constructor.Invoke(新的对象[] {左});
}
}
如果(右边是IComparable的< TLeft>)
{
变种构造=
的typeof(RightComparable<,>)。MakeGenericType( typeof运算(TLeft)的typeof(TRight))
.GetConstructor(新[] {typeof运算(TLeft)});
如果(构造!= NULL)
{
回报率(BaseComparable< TLeft,TRight>)constructor.Invoke(新的对象[] {左});
}
}
抛出新的ArgumentException();
}
然后,你可以说
类巴兹
{
公共int值{搞定;组; }
}
类酒吧:IComparable的<&巴兹GT;
{
公共int值{搞定;组; }
INT IComparable的<&巴兹GT; .CompareTo(巴兹等)
{
返回Value.CompareTo(other.Value);
}
}
// ...
无功吧=新的酒吧{值= 1};
变种巴兹=新巴兹{值= 1};
VAR compBaz = baz.AsComparableFor(巴);
VAR compBar = bar.AsComparableFor(巴兹);
好极了,类型推断的工作原理完全符合市场预期。
从上面的接受的答案,但是,
公共静态类可比
{
适配公共静态BaseComparable< TLeft,TRight>
AsComparableFor< TLeft,TRight>(这IComparable的< TRight>左,右TRight)
式TLeft:IComparable的< TRight>
{
如果(左边是TLeft)
{
如果(左边是IComparable的< TRight>)
{
返回新LeftComparable< TLeft,TRight> ((TLeft)左);
}
}
抛出新InvalidCastException的();
}
公共静态BaseComparable< TLeft,TRight>
AsComparableFor< TLeft,TRight>(这TLeft左,IComparable的< TLeft>右)
式TRight:IComparable的< TLeft>
{
如果(左边是TLeft)
{
如果(右边是IComparable的< TLeft>)
{
返回新RightComparable< TLeft,TRight> ((TLeft)左);
}
}
抛出新InvalidCastException的();
}
}
要求您明确说明类型参数:
// bar.AsComparableFor(巴兹);
//baz.AsComparableFor(bar); //不能编译
bar.AsComparableFor<酒吧,巴兹>(巴兹);
baz.AsComparableFor<巴兹,酒吧>(巴); //是否编译
这方面的一个重要组成部分是使图书馆尽可能无痛,和我觉得不必指定类型的失败有点。
是否有中间立场?我能得到干净,从原来的类型推断的实力公认的答案无反射代码
编辑:<?A HREF =HTTPS://gist.github .COM / RoadieRich / 8ffbf6ac08fedd39c528相对=nofollow>完整的代码可以在这个主旨找到。
我可以得到清洁,从原来的?
块引用>
的类型推断的实力公认的答案无反射代码您不能。事实上,因为它涉及到价值型拳击接受的答案是不好的。
所以,再一次,你无法避免反射。你虽然可以做的是为最小化使用相同的技术,因为在反射
EqualityComparer< T> .DEFAULT
的实施,的Comparer< T> .DEFAULT
等,唯一的区别是,与其创建一个单一实例,我们将创建一个单厂委托:公共抽象类BaseComparable< TLeft,TRight>
{
公共静态只读Func键< TLeft,BaseComparable< TLeft,TRight>>工厂= CreateFactory();
私有静态Func键< TLeft,BaseComparable< TLeft,TRight>> CreateFactory()
{
型genericTypeDefinition;
如果(typeof运算(IComparable的< TRight>)IsAssignableFrom(typeof运算(TLeft))。)
genericTypeDefinition = typeof运算(LeftComparable<,>);
,否则如果(typeof运算(IComparable的< TLeft>)IsAssignableFrom(typeof运算(TRight))。)
genericTypeDefinition = typeof运算(RightComparable<,>);
,否则
抛出新的ArgumentException();
变量参数= Expression.Parameter(typeof运算(TLeft),值);
变种体= Expression.New(genericTypeDefinition
.MakeGenericType(typeof运算(TLeft)的typeof(TRight))
.GetConstructor(新[] {typeof运算(TLeft)}),参数);
VAR波长= Expression.Lambda<&Func键LT; TLeft,BaseComparable< TLeft,TRight>>>(机身,参数);
返回lambda.Compile();
}
}
公共静态类BaseComparable
{
公共静态BaseComparable< TLeft,TRight> AsComparableFor< TLeft,TRight>(这TLeft左,右TRight)
{
返回BaseComparable< TLeft,TRight> .Factory(左);
}
}
This is an extension from this question, which had an answer that works in that specific case.
My actual code looks more like this:
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) { } }
If you use the equivalent reflection code to what I posted, it works great:
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(); }
Then you can say
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);
Fantastic, type inference works exactly as expected.
The adaptation from the accepted answer above, however,
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(); } }
Requires you to explicitly state the type arguments:
//bar.AsComparableFor(baz); //baz.AsComparableFor(bar); //Does not compile bar.AsComparableFor<Bar, Baz>(baz); baz.AsComparableFor<Baz, Bar>(bar); // Does compile
A big part of this was to make the library as painless as possible, and I feel having to specify types defeats that somewhat.
Is there a middle ground? Can I get the cleaner, reflectionless code from the accepted answer with the type inference strength of the original?
Edit: full code can be found in this gist.
解决方案Can I get the cleaner, reflectionless code from the accepted answer with the type inference strength of the original?
You can't. Actually the accepted answer is not good because it involves value type boxing.
So again, you cannot avoid reflection. What you can do though is to minimize the reflection by using the same technique as in
EqualityComparer<T>.Default
implementation,Comparer<T>.Default
etc. The only difference would be that instead of creating a singleton instance, we'll create a singleton factory delegate: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); } }
这篇关于与反向约束的仿制药之间的类型推断的文章就介绍到这了,希望我们推荐的答案对大家有所帮助,也希望大家多多支持IT屋!