5

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??

Community
  • 1
  • 1
q0987
  • 34,938
  • 69
  • 242
  • 387
  • 1
    Have you searched? You may need to use `+in` instead of `in` if the search engine doesn't take the word `in`. – BoltClock Jun 01 '11 at 15:28
  • ref would not apply in this context – Jodrell Jun 01 '11 at 15:28
  • @BoltClock, I did search through google by using 'C# parameter ref out in' or 'C# in parameter' and no useful info was returned. – q0987 Jun 01 '11 at 15:42
  • 2
    See my series of articles on the design of this feature. (Start from the bottom and work your way up; part eight is the part that actually answers your question.) http://blogs.msdn.com/b/ericlippert/archive/tags/covariance+and+contravariance/default.aspx – Eric Lippert Jun 01 '11 at 16:23

7 Answers7

5

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).

Marc Gravell
  • 1,026,079
  • 266
  • 2,566
  • 2,900
  • your description for in and out is easy to understand. But I still cannot make the connect between T and interface IGobbler. Based on my understanding, covariance and contravariance are used when you assign a method to a delegate. As Eric indicated in his article, they are just projections. Also, is it true that we only specify in and out when we define a delegate? – q0987 Jun 01 '11 at 18:22
  • @q0987 no, that is incorrect; delegates ***or interfaces***. Most notably, it is now `IEnumerable` – Marc Gravell Jun 01 '11 at 18:29
  • In the case of `IGobbler`, that will enforce contravariance on that interface. It wont let you add an "outward" API (a way of obtaining a `T`) as that would break this rule. But it will also allow implicit contravariant casts, which is cute. Variance is entirely optional. – Marc Gravell Jun 01 '11 at 18:32
  • To give a covariant example, if you have an `IEnumerable`, you can now assign that to an `IEnumerable` since any `string` is also an `object` - only works for references though – Marc Gravell Jun 01 '11 at 18:34
4

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.

Gabe
  • 84,912
  • 12
  • 139
  • 238
  • To me, it seems that this 'out' is different from what I knew the out parameter in the method parameter list. That out means your method must assign value to that variable before the function returns. – q0987 Jun 01 '11 at 15:50
  • @q0987: Yes, that is use of `out` in a different context. In that context it appears in a function's formal parameter list. In this context it appears in an interface's type parameter list. – Gabe Jun 01 '11 at 15:53
2

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/

manojlds
  • 290,304
  • 63
  • 469
  • 417
2

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;    
    }
}
James Michael Hare
  • 37,767
  • 9
  • 73
  • 83
  • I must be missing something. I don't see `in` anywhere in your code (only in the comment). – comecme Jun 01 '11 at 16:09
  • @comecme, I assume that James just simply uses the existing interface IGobbler – q0987 Jun 01 '11 at 18:25
  • please correct me if I am wrong. When assign Action r = g.gobble, the contravariance happens. When passing Rectangle to g.gobble, the covariance happens. Is that correct? – q0987 Jun 01 '11 at 18:28
2

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.

Jes Hansen
  • 21
  • 1
  • how this statement works? IGobbler dg = new QuadrupedGobbler(); Does class QuadrupedGobbler implement the interface so that we can do so? In fact, this is the place where I really feel confused. – q0987 Jun 01 '11 at 16:12
  • 1
    QuadrupedGobbler implements the generic IGobbler interface which through covariance specifically the signature of the interface which will look something like this `public interface IGobbler...` allows the IGobbler type to contain a QuadrupedGobbler because it implements IGobbler. If you understand polymorphism then its not too dissimilar a concept. – Jake Aitchison Jul 11 '13 at 21:10
1

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.

Dan Bryant
  • 27,329
  • 4
  • 56
  • 102
0

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

Perplexed
  • 877
  • 2
  • 19
  • 32