泛型方法可以使用逆变/协变的类型? [英] A generic method can use contravariant/covariant types?

查看:208
本文介绍了泛型方法可以使用逆变/协变的类型?的处理方法,对大家解决问题具有一定的参考价值,需要的朋友们下面随着小编来一起学习吧!

问题描述

我在书面方式T4模板使用它在一个特殊任务的通用方法。该方法应该允许我使用特定类型从一般的接口。我想到了以下特征:

 接口IGreatInterface {
对象aMethodAlpha< U>(U参数),其中U: IAnInterface;
对象aMethodBeta(IAnInterface参数)
}

公共类AnInterestingClass:IAnInterface {}

当我尝试实施 IGreatInterface 编译器标志 aMethodBeta错误()因为我做了我的T4编写使用 IAnInterface 的子类型的方法(即我想要实现像这样的方法:对象aMethodBeta(AnInterestingClass参数)



方法 aMethodAlpha< U>()可以使用,但是不是干净,因为我想,因为我的T4有可能产生一些额外的代码。我(也许错误地)
建议该方法中,其具有由一T4来完成的实施方案,可将结果
对象aMethodAlpha&所述; AnInterestingClass>(AnInterestingClass参数)



我在想,一般的方法不支持逆变类型,但我不知道;我想,这是编译防止编码器使用具有在一般类型没有定义的方法的特定类型的方式...



<醇>
  • 是否泛型方法必须使用的确切类型正在实施的时候?

  • 有没有什么绝招来改变这种行为?


  • 解决方案

    这个问题是相当混乱。让我看看,如果我可以澄清。




    当我尝试实施 IGreatInterface 编译器标志 aMethodBeta错误(),因为我做了使用 IAnInterface 的子类型我想这个方法实现这样的方法:对象aMethodBeta(AnInterestingClass参数)




    这是不合法的。简化有点:

     类食品{} 
    类水果:食品{}
    类肉类食物{ }
    接口IEater
    {
    无效吃(食品食品);
    }
    类素食:IEater
    {
    公共无效吃(果果)
    }



    素食做不履行 IEater 的合同。您应该能够通过的任何的食物吃,但素食只接受水果。 。C#不支持的的虚拟方法形式参数的协方差的,因为这不是类型安全的。



    现在,你可能会然后说,这个怎么样:

     接口IFruitEater 
    {
    无效吃(果果)
    }
    类杂食:IFruitEater
    {
    公共无效吃(食品食品);
    }

    现在我们已经得到了类型安全; 杂食动物可以作为一个 IFruitEater ,因为杂食动物 CAN吃水果,以及任何其他食物。



    不幸的是,C#不支持的虚方法的形参类型逆变的,即使这样做是理论类型安全。很少有语言都支持这一点。



    同样,C#不支持的虚拟方法的返回类型差异的无论是。



    我不知道,其实回答您的问题或没有。 ?你能澄清这个问题。



    更新:



    什么:

     接口IEater 
    {
    无效吃< T>(T t)其中T:食品;
    }
    类素食:IEater
    {
    //我只想吃水果!
    公共无效吃<水果GT;(水果食品){}
    }



    不,这不是法律无论是。 IEater 的合同是,你会提供的方法吃< T> ,可以采取任何 T 这是一个食物。你不能的部分的实施合同,比你能做到这一点的更多:

     接口IAdder 
    {
    INT加入(INT X,int y)对;
    }
    类加法:IAdder
    {
    //我只知道如何添加两个!
    公众诠释添加(2,int y)对{...}
    }

    不过,你可以这样做:

     接口IEater< T>其中T:食品
    {
    无效吃(T T);
    }
    类素食:IEater<水果GT;
    {
    公共无效吃(果果){}
    }

    这是完全合法的。但是,你不能这样做:

     接口IEater< T>其中T:食品
    {
    无效吃(T T);
    }
    类杂食:IEater<水果GT;
    {
    公共无效吃(食品食品){}
    }

    由于一遍,C#不支持虚拟方法的形参逆变或协方差。



    需要注意的是C#的确实的支持的参数多态性协的这样做时被称为是类型安全。例如,这是合法的:

     的IEnumerable<水果GT;水果=什么; 
    IEnumerable的<食品及GT;食品水果=;



    果的序列可以用作食品的序列。或者,

     的IEnumerable<水果GT; fruitComparer =什么; 
    IComparable的<苹果> appleComparer = fruitComparer;

    如果你有什么事情,可以比较任意两个水果那么它可以比较任意两个苹果。



    然而,当以下为真这样的协变和逆变是唯一合法的:(1)中的变化可证明是类型安全的,(2)型加方差的作者注释说明所需的合作和反差异,(3)所涉及的不同类型的参数都是引用类型,(4)通用类型是一个委托或接口。


    I'm writting a generalized method to use it in a special task at a T4 template. The method should allow me to use specialized types from a general interface. I thought about the following signatures:

    interface IGreatInterface {
        Object aMethodAlpha<U>(U parameter) where U : IAnInterface;
        Object aMethodBeta(IAnInterface parameter)
    }
    
    public class AnInterestingClass : IAnInterface{}
    

    When I try to implement IGreatInterface the compiler flags an error for aMethodBeta() because I've made my T4 to write that method using a subtype of IAnInterface (i.e. I want to implement that method like this: Object aMethodBeta(AnInterestingClass parameter)).

    Method aMethodAlpha<U>() can be used but is not as clean as I want because my T4 has to generate some extra code. I (perhaps wrongly) propose that an implementation of that method, which has to be done by a T4, could be
    Object aMethodAlpha<AnInterestingClass>(AnInterestingClass parameter).

    I'm thinking that generic methods do not support contravariant types but I'm not sure; I suppose that It's the way the compiler prevents the coder to use a specific type having a method not defined in the general type...

    1. Does a generic method have to use the exact type when being implemented?
    2. Is there any trick to change this behavior?

    解决方案

    This question is quite confusing. Let me see if I can clarify it.

    When I try to implement IGreatInterface the compiler flags an error for aMethodBeta() because I've made that method using a subtype of IAnInterface I want to implement that method like this: Object aMethodBeta(AnInterestingClass parameter).

    That's not legal. Simplifying somewhat:

    class Food {}
    class Fruit : Food {}
    class Meat : Food {}
    interface IEater
    {
        void Eat(Food food);
    }
    class Vegetarian : IEater
    {
        public void Eat(Fruit fruit);
    }
    

    Class Vegetarian does not fulfill the contract of IEater. You should be able to pass any Food to Eat, but a Vegetarian only accepts Fruit. C# does not support virtual method formal parameter covariance because that is not typesafe.

    Now, you might then say, how about this:

    interface IFruitEater
    {
        void Eat(Fruit fruit);
    }
    class Omnivore : IFruitEater
    {
        public void Eat(Food food);
    }
    

    Now we have got type safety; Omnivore can be used as an IFruitEater because an Omnivore can eat fruit, as well as any other food.

    Unfortunately, C# does not support virtual method formal parameter type contravariance even though doing so is in theory typesafe. Few languages do support this.

    Similarly, C# does not support virtual method return type variance either.

    I'm not sure if that actually answered your question or not. Can you clarify the question?

    UPDATE:

    What about:

    interface IEater
    {
        void Eat<T>(T t) where T : Food;
    }
    class Vegetarian : IEater
    {
        // I only want to eat fruit!
        public void Eat<Fruit>(Fruit food) { }
    }
    

    Nope, that's not legal either. The contract of IEater is that you will provide a method Eat<T> that can take any T that is a Food. You cannot partially implement the contract, any more than you could do this:

    interface IAdder
    {
        int Add(int x, int y);
    }
    class Adder : IAdder
    {
        // I only know how to add two!
        public int Add(2, int y){ ... }
    }
    

    However, you can do this:

    interface IEater<T> where T : Food
    {
        void Eat(T t);
    }
    class Vegetarian : IEater<Fruit>
    {
        public void Eat(Fruit fruit) { }
    }
    

    That is perfectly legal. However, you cannot do:

    interface IEater<T> where T : Food
    {
        void Eat(T t);
    }
    class Omnivore : IEater<Fruit>
    {
        public void Eat(Food food) { }
    }
    

    Because again, C# does not support virtual method formal parameter contravariance or covariance.

    Note that C# does support parametric polymorphism covariance when doing so is known to be typesafe. For example, this is legal:

    IEnumerable<Fruit> fruit = whatever;
    IEnumerable<Food> food = fruit;
    

    A sequence of fruit may be used as a sequence of food. Or,

    IEnumerable<Fruit> fruitComparer = whatever;
    IComparable<Apples> appleComparer = fruitComparer;
    

    If you have something that can compare any two fruits then it can compare any two apples.

    However, this kind of covariance and contravariance is only legal when all of the following are true: (1) the variance is provably typesafe, (2) the author of the type added variance annotations indicating the desired co- and contra-variances, (3) the varying type arguments involved are all reference types, (4) the generic type is either a delegate or an interface.

    这篇关于泛型方法可以使用逆变/协变的类型?的文章就介绍到这了,希望我们推荐的答案对大家有所帮助,也希望大家多多支持IT屋!

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