参数列表中的in修饰符的含义是什么

本文关键字:是什么 列表 in 参数 | 更新日期: 2023-09-27 17:58:15

我在中看到了以下用法:

协方差和反方差真实世界示例

interface IGobbler<in T> {
    void gobble(T t);
}

我不明白中的用法代表什么。它是否与refout有关系??

参数列表中的in修饰符的含义是什么

4.0中的inout修饰符是强制(或者更确切地说:启用)协方差和逆变差所必需的。

如果添加in,则只允许在向内(逆变)位置使用T,所以像Add(T obj)这样的东西是可以的,但T this[int index] {get;}不是,因为这是向外(协变)位置。

这对于4.0中的方差特征至关重要。有了方差,refout都不可用(它们都不可用,因此:两者都不可用)。

忽略您对refout的了解,因为它与此上下文无关。在这种情况下,in意味着T将只出现在函数名称的右侧(即,在像void gobble(T t)这样的正式参数列表中)。如果它说out,那么T将只出现在函数名称的左边(即返回值如T foo(int x))。默认值(不指定任何内容)允许T出现在任意位置。

在C#4.0中,Contravariance允许例如,要强制转换为的IComparer <X>IComparer <Y>,即使Y是派生的类型X。为了实现这一目标应使用In修饰符进行标记。

    public interface IComparer<in T> {
        public int Compare(T left, T right);
    }

看看这里的例子和解释:

http://www.csharphelp.com/2010/02/c-4-0-covariance-and-contravariance-of-generics/

in修饰符告诉该类型是反变的,可以隐式转换为更窄的类型。请注意,在下面的示例中,即使狼吞虎咽采用Shape,它也可以分配给Action<Rectangle>,因为我们已经声明它是反变体。这是因为任何调用委托并将其传递给Rectangle的人显然也可以将Rectangle传递给采用Shape的方法。

使用inout时有一些规则,但简而言之,这就是它所能实现的。

例如:

public class Shape    {    } 
public class Rectangle : Shape { }
public interface IGobbler<Shape>
{
    void gobble(Shape shape);
}
public class Gobbler : IGobbler<Shape>
{
    public void gobble(Shape r)     { }     
}
public static class Program
{
    public static void Main()
    {
        var g = new Gobbler();
        // notice can implictly convert to a narrower type because of the 'in' keyword
        Action<Rectangle> r = g.gobble;    
    }
}

in和out与ref和out没有任何关系。

in关键字用于描述接口的in实例将使用T实例。在该示例中,您链接了行

IGobbler<Donkey> dg = new QuadrupedGobbler();

创建一个你可以给驴子喂食的吞噬程序,尽管驴子不是一个QuadrupledCreature,但它是从中派生的。因此,你可以使用一个更专业的实例而不是基类作为参数。

out关键字的工作原理基本相同,只是它用来描述一种产生东西的东西,而不是用来消费它

在同一示例中,行

ISpewer<Rodent> rs = new MouseSpewer();

创建一个ISpewer,当被调用时会喷出一只老鼠。鼠标不是啮齿类动物,而是从它派生而来的,因此您可以使用生成类来生成比接口声明的更专业的实例。

请注意,在这两种情况下,最专业化的类是如何交换的。当使用in关键字时,您使用专用类作为接口上的泛型参数,而在out情况下,您使用基类作为泛型参数来告诉编译器,即使您创建了一个更专用的类,它也应该像对待基类一样对待它。

我喜欢把它看作是消费和生产,因为这些对大多数开发人员来说都是熟悉的隐喻。一种采用IGobbler<Cow>的方法也可以接受IGobbler<Animal>,因为可以吞食(吃掉)任何动物的Gobler也可以吞食奶牛。这里的Gobler是特定类型动物的消费者,因此它使用in标签。

上面的情况(相反)可能看起来有违直觉,但请从想要Gobbler<Cow>的RestaurantOwner的角度来思考。如果Gobler只会狼吞虎咽地吃猪,而餐馆老板试图喂他一头牛,那就行不通了。他只能接受不那么挑剔的Gobblers,所以Gobbler<Animal>Gobbler<Herbivore>很好。

另一方面,假设您有一个出售动物的Farmer<Animal>(具有返回IEnumerable<Animal>的Farm方法。)如果您有一位想要Buy(IEnumerable<Animal>)的买家,那么它可以接受Farmer<Cow>.Farm(),因为买家愿意购买任何生产的动物,而奶牛是动物。这里的农夫是一种特定类型动物的生产者,所以它使用"out"标签。

IN关键字告诉编译器我们只想使用T作为输入值。

它不允许从IGobbler到IGobbler

进行强制转换