与反向约束的仿制药之间的类型推断 [英] Type Inference between generics with reversed constraints

查看:124
本文介绍了与反向约束的仿制药之间的类型推断的处理方法,对大家解决问题具有一定的参考价值,需要的朋友们下面随着小编来一起学习吧!

问题描述

这是从这个问题,其中有在特定情况下,工作的一个答案的扩展。



我实际的代码看起来更像是这样的:

 公共抽象类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屋!

查看全文
登录 关闭
扫码关注1秒登录
发送“验证码”获取 | 15天全站免登陆