I saw the following usage开发者_开发百科 of in:
Covariance and contravariance real world example
interface IGobbler<in T> {
void gobble(T t);
}
I don't understand what the usage of in stands for. Does it have relationship with ref, out??
The in
and out
modifiers in 4.0 are necessary to enforce (or rather: enable) covariance and contravariance.
If you add in
, you are only allowed to use T
in inwards (contravariant) positions - so things like Add(T obj)
is fine, but T this[int index] {get;}
is not as this is an outwards (covariant) position.
This is essential for the variance features in 4.0. With variance, ref
and out
are both unavailable (they are both, and as such: neither).
Ignore what you know about ref
and out
because it's unrelated to this context. In this case in
means that the T
will only appear on the right hand side of function names (i.e. in formal parameter lists like void gobble(T t)
). If it said out
, then T
would only appear to the left of function names (i.e. return values like T foo(int x)
). The default (not specifying anything) allows T
to appear in either place.
In C# 4.0, Contravariance allows for example, IComparer
<X>
to be cast to IComparer<Y>
even if Y is a derived type of X. To achieve this IComparer should be marked with the In modifier.
public interface IComparer<in T> {
public int Compare(T left, T right);
}
Have look here for example and explanation:
http://www.csharphelp.com/2010/02/c-4-0-covariance-and-contravariance-of-generics/
The in
modifier tells you that the type is contravariant and can be implicitly converted to a narrower type. Notice in the example below that even though gobble takes a Shape
, it can be assigned to an Action<Rectangle>
because we've declared it to be contravariant. This is because anyone calling the delegate and passing it a Rectangle can obviously also pass the Rectangle to a method that takes a Shape as well.
There are some rules for when you use in
, and out
, but that's what it enables in a nutshell.
For example:
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;
}
}
the in and out does not have anything to do with ref and out.
The in keyword is used to describe that in instance of the interface will consume an instance of T. In the example you linked the line
IGobbler<Donkey> dg = new QuadrupedGobbler();
creates a gobbler that you can feed Donkeys to, eventhough the Donkey isn't a QuadrupledCreature, but it derives from it. So you are able to use a more specialized instance instead of the base class as argument.
The out keyword works much the same way, except it's used to describe a thing that produces stuff instead of comsuming it.
In the same example, the line
ISpewer<Rodent> rs = new MouseSpewer();
creates an ISpewer, which when called spews a mouse. A mouse is not a rodent, but derives from it, so you are able to use a producing class that produces more specialized instance than what the interface declares.
Notice how the way the most specialized class is swapped in the two cases. When using the in keyword, you use the specialized class as the generic argument on the interface, whereas in the out case, you use the base class as the generic argument to tell the compiler, that eventhough you create a more specialized class, it should treat it like the base class.
I like to think of it as consumption and production, as these are familiar metaphors for most developers. A method that takes an IGobbler<Cow>
can also accept an IGobbler<Animal>
, because a Gobbler that can gobble (consume) any Animal can also gobble a Cow. The Gobbler here is a consumer of a specific type of Animal, so it uses the in
tag.
The above case (contravariance) can seem counter-intuitive, but think about it from the perspective of the RestaurantOwner who wants a Gobbler<Cow>
. If a Gobbler will only gobble Pigs and the RestaurantOwner tries to feed him a Cow, it won't work. He can only accept Gobblers that are less picky, so a Gobbler<Animal>
or Gobbler<Herbivore>
works fine.
On the other hand, suppose you have a Farmer<Animal>
that sells Animals (having a Farm method that returns IEnumerable<Animal>
.) If you have a Purchaser that wants to Buy(IEnumerable<Animal>)
, then it can accept Farmer<Cow>.Farm()
, as the Purchaser is willing to buy any produced Animal and Cows are Animals. The Farmer here is a producer of a specific type of Animal, so it uses the `out' tag.
The IN keyword tells the compiler that we only want to use T as an input value.
It will not allow casting from say, IGobbler to IGobbler
精彩评论