5

In the CLR via c# third edition there is an example that I cant seem to make sense of:

Invariant Meaning that that generic type parameter cannot be changed. I have shown only invariant generic type parameters so far in this chapter. n

Contravariant Meaning that the generic type parameter can change from a class to a class derived from it. In C#, you indicate contravariant generic type parameters with the in keyword.

Contravariant generic type parameters can appear only in input positions such as a method’s argument. n Covariant Meaning that the generic type argument can change from a class to one of its base classes. In C#, you indicate covariant generic type parameters with the out keyword. Covariant generic type parameters can appear only in output positions such as a method’s return type.

The author then goes on to give this example:

public delegate TResult Func<in T, out TResult>(T arg);

Here, the generic type parameter T is marked with the in keyword, making it contravariant; and the generic type parameter TResult is marked with the out keyword, making it covariant

Here is where I run into the issue on the following page(292) he then goes on the say the opposite when using an interface.

When using delegates that take generic arguments and return values, it is recommended to always specify the in and out keywords for contravariance and covariance whenever >possible, as doing this has no ill effects and enables your delegate to be used in more scenarios. Like delegates, an interface with generic type parameters can have its type parameters be contravariant or covariant. Here is an example of an interface with a contravariant >generic type parameter:

public interface IEnumerator<out T> : IEnumerator {
Boolean MoveNext();
T Current { get; }
}

Since T is contravariant, it is possible to have the following code compile and run >successfully:

// This method accepts an IEnumerable of any reference type
Int32 Count(IEnumerable<Object> collection) { ... }
...
// The call below passes an IEnumerable<String> to Count
Int32 c = Count(new[] { "Grant" });

In the second example he uses the out key word (IEnumerator<out T>) and then calls it contravariant. Is this correct or am I missing something. Is there a difference defining a contravariant & covariant in an interface? I have been to Oreilly's website regarding this book and this is not listed.

skaffman
  • 398,947
  • 96
  • 818
  • 769
Chris G
  • 469
  • 8
  • 14
  • It's important to note that the design of `Delegate.Combine` is fundamentally incompatible with variance. If a routine has an initially-null field of delegate type `Action`, and it tries to `Combine` it with an `Action`, the field of type `Action` will hold a reference to an `Action`. If one then tries to `Combine` that with an `Action`, the attempt will fail. For this reason, delegates types which may ever be used with `Combine` should not be covariant nor contravariant. It's too bad delegate types don't define their own `Combine` methods, since... – supercat Oct 19 '12 at 19:20
  • ...a method `Action.Combine` could have safely taken the `Action` and converted it to a type `Action`, knowing that the result of such a conversion would only be used by code which would feed it a `Cat`. – supercat Oct 19 '12 at 19:22

2 Answers2

20

out = covariant and in = contravariant.

Any words to the opposite are a mistake in my book which I'll correct in a future edition.

jjnguy
  • 136,852
  • 53
  • 295
  • 323
Jeffrey Richter
  • 216
  • 1
  • 2
9

That's a mistake. It's definitely an example of covariance. There's no difference in meaning of covariance and contravariance between delegates and interfaces.

I suggest you email O'Reilly to report the error.

Jon Skeet
  • 1,421,763
  • 867
  • 9,128
  • 9,194