参数列表中的in修饰符的含义是什么
本文关键字:是什么 列表 in 参数 | 更新日期: 2023-09-27 17:58:15
我在中看到了以下用法:
协方差和反方差真实世界示例
interface IGobbler<in T> {
void gobble(T t);
}
我不明白中的用法代表什么。它是否与ref、out有关系??
4.0中的in
和out
修饰符是强制(或者更确切地说:启用)协方差和逆变差所必需的。
如果添加in
,则只允许在向内(逆变)位置使用T
,所以像Add(T obj)
这样的东西是可以的,但T this[int index] {get;}
不是,因为这是向外(协变)位置。
这对于4.0中的方差特征至关重要。有了方差,ref
和out
都不可用(它们都不可用,因此:两者都不可用)。
忽略您对ref
和out
的了解,因为它与此上下文无关。在这种情况下,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的方法。
使用in
和out
时有一些规则,但简而言之,这就是它所能实现的。
例如:
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