2011-06-01 42 views
4

我看到的在下面的用法:参数列表中的修饰符的含义是什么?

Covariance and contravariance real world example

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

我不明白的代表使用。是否与编号,

+1

您是否搜索过?如果搜索引擎不采用“in”这个词,你可能需要使用'+ in'而不是'in'。 – BoltClock 2011-06-01 15:28:26

+0

ref在这方面不适用 – Jodrell 2011-06-01 15:28:41

+0

@BoltClock,我通过使用'C#parameter ref in'或'C#in parameter'搜索谷歌并且没有有用的信息被返回。 – q0987 2011-06-01 15:42:00

回答

3

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

+0

为便于使用术语。像这个答案一样。 – JonH 2011-06-01 15:40:57

+0

对我来说,这个'out'与我在方法参数列表中知道out参数不同。这意味着您的方法必须在函数返回之前为该变量赋值。 – q0987 2011-06-01 15:50:09

+0

@ q0987:是的,这是在不同的上下文中使用'out'。在这种情况下,它出现在函数的形式参数列表中。在这种情况下,它出现在界面的类型参数列表中。 – Gabe 2011-06-01 15:53:13

5

4.0中的inout修饰符对强制执行(或者说:启用)协方差和逆变是必需的。

如果添加in,您只能在向内(逆变)位置使用T - 所以像Add(T obj)是好的,但T this[int index] {get;}因为这是一个向外(协)的位置。

这对于4.0中的方差特征很重要。有差异的话,refout都不可用(它们都是,都是这样:都不)。

+0

您的进出口说明很容易理解。但我仍然无法连接T和接口IGobbler 。根据我的理解,将方法分配给委托时会使用协方差和逆变。正如Eric在他的文章中指出的那样,它们只是预测。另外,当我们定义一个委托时,我们是否只指定了内部和外部? – q0987 2011-06-01 18:22:50

+0

@ q0987不,这是不正确的;代表***或接口***。最值得注意的是,它现在是'IEnumerable ' – 2011-06-01 18:29:44

+0

在'IGobbler '的情况下,这将强制在该接口上进行反转。它不会让你添加一个“向外”的API(一种获得'T'的方法),因为这会破坏这个规则。但它也将允许隐含的逆转演员,这是可爱的。差异完全是可选的。 – 2011-06-01 18:32:40

2

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;  
    } 
} 
+0

我必须缺少一些东西。我在代码中的任何地方都看不到(仅在评论中)。 – comecme 2011-06-01 16:09:39

+0

@comecme,我假设詹姆斯只是简单地使用现有的接口IGobbler q0987 2011-06-01 18:25:10

+0

如果我错了,请纠正我。当分配动作 r = g.gobble时,发生反转。将Rectangle传递给g.gobble时,会发生协方差。那是对的吗? – q0987 2011-06-01 18:28:10

0

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

它不会允许来自比方说,IGobbler铸造IGobbler

1

我喜欢把它看作是消费和生产,因为这些对于大多数开发人员熟悉的隐喻。需要IGobbler<Cow>的方法也可以接受IGobbler<Animal>,因为可以吞噬(消耗)任何动物的狼吞虎咽也可以吞噬牛。这里的Gobbler是一种特定类型动物的消费者,所以它使用in标签。

上述案例(反变换)看起来可能与直觉相反,但从RestaurantOwner的角度考虑,他想要一个Gobbler<Cow>。如果一个戈布勒尔只会吞噬猪,并且该餐厅的所有者试图给他喂牛,那么它就行不通。他只能接受不那么挑剔的戈布勒,所以Gobbler<Animal>Gobbler<Herbivore>工作正常。

在另一方面,假设你有一个Farmer<Animal>出售的动物(具有返回IEnumerable<Animal>一个农场方法。)如果你有想要Buy(IEnumerable<Animal>)一个买方,那么它可以接受Farmer<Cow>.Farm(),作为买方愿意购买任何生产的动物和母牛都是动物。这里的农民是特定类型动物的生产者,所以它使用'out'标签。

2

进出与参考和输出没有任何关系。

in关键字被用于描述在该接口的实例将消耗T的一个实例在该实例中,你链接的线

​​

创建火鸡,可以喂驴到沉绵的驴不是一个QuadrupledCreature,但它来自它。所以你可以使用更专门的实例而不是基类作为参数。

out关键字的工作方式大致相同,只是它用于描述产生东西而不是包含它的东西。

在同一示例中,线

ISpewer<Rodent> rs = new MouseSpewer(); 

创建ISpewer,其调用时喷出鼠标。鼠标不是啮齿动物,而是从中派生出来的,因此您可以使用生成的类生成比接口声明的更专用的实例。

请注意在两种情况下,最特殊的类是如何交换的。当使用in关键字时,你使用专用类作为接口的通用参数,而在这种情况下,你使用基类作为通用参数来告诉编译器,尽管你创建了一个更专门的类,它应该把它当作基类来对待。

+0

这个声明是如何工作的? IGobbler dg = new QuadrupedGobbler();类QuadrupedGobbler是否实现了接口,以便我们可以这样做?事实上,这是我真正感到困惑的地方。 – q0987 2011-06-01 16:12:19

+1

QuadrupedGobbler实现通用的IGobbler接口,该接口通过协方差特别是接口的签名,看起来像这样'public interface IGobbler ...'允许IGobbler类型包含QuadrupedGobbler,因为它实现了IGobbler。 如果你了解多态性,那么它的概念并不太相似。 – 2013-07-11 21:10:35

相关问题