What is the difference between the declaration forms of Generics with out and in?

Asked

Viewed 175 times

4

What is the difference between Generics statements when using out and in?

Example:

public class GenericClass<T> { }

public class GenericClassOut<out T> { }

public class GenericClassIn<T in> { }
  • I believe your question has already been answered at: &#xD; http://stackoverflow.com/questions/10956993/out-t-vs-t-in-generics

  • 1

    @Ivanteles Yes, but it would be nice to have an answer in Portuguese. Feel free to write one, or even translate some of the English (quoting the source, of course).

  • And there’s the in too, please!

  • 1

    Related: "What are covariance and countervariance?". I do not consider this question duplicated (because these concepts are more general, applying not only to generic types but also to other contexts, such as input and output of functions), so I gave a more contextualized answer below, without getting into the merit of explaining in detail the concepts.

1 answer

5


The out and the in make the type generic covariant and countervariant, respectively.

An example of a covariant type is the IEnumerable:

public interface IEnumerable <out T>: IEnumerable
{
    IEnumerator<T> GetEnumerator();
}

IEnumerable<string> strings = new List<string>();
IEnumerable<object> objects = strings;

Source, Source

As the function of a IEnumerable is to iterate over the members of a list, and every object of a more specific type can be assigned to a variable of a more general type (i.e. an object string can be stored in a variable object), then an object that "produces" a string sequence can also be used as "an object that produces a sequence of objects". The returned elements will be the same (the strings in the list), but their static type will be object and not string.

The opposite is not true: a IEnumerable that produces a sequence of objects cannot be used [securely] in a context that expects a string sequence (since it can also produce things that are not strings).

An example of a contravariant type is IComparer:

public interface IComparer<in T>
{
    int Compare(T x, T y);
}

IComparer<object> objects = ...;
IComparer<string> strings = objects;

In this case the relationship is opposite: if a IComparer is able to compare any objects, it is also able to compare strings (because a string is an object). So it can be used in a context that requires a string comparator. The opposite is not true, because a comparator who only knows how to compare strings cannot be used to compare arbitrary objects.

Finally, an example of type invariant (which is neither co- nor contravariant) is itself IList:

IList<string> strings = new List<string>();
IList<object> objects = new List<objects>();

How much the list accepted how much returns objects of the specified type cannot be assigned strings = objects (because the list could already contain elements that are not strings) nor objects = strings (because the list should not be able to receive elements that are not strings). Conversion in both directions is prohibited, so as to ensure type safety. So that neither the in nor the out shall be used in the generic type declaration:

public interface IList<T> ...
  • Thanks for the reply. I had to read it several times to understand!! = D rs.. Thanks!

Browser other questions tagged

You are not signed in. Login or sign up in order to post.