Variance rules in C#

断了今生、忘了曾经 提交于 2019-12-04 03:09:08

You are right that the last rule is the hardest one to understand but I assure you it is not ambiguous.

An example or two will help. Consider this type declaration:

interface I<in T, out U, V> { ... }

Is this type covariantly valid?

I<string, object, int> { }

Let's go through our definition.

To determine if it is, we examine each type argument differently, depending on whether the corresponding type parameter was declared as covariant (out), contravariant (in), or invariant (neither).

OK, so the type arguments are string, object and int. The corresponding parameters are in T, out U and V, respectively.

If the ith type parameter was declared as covariant (out), then Ti must be valid covariantly.

The second type parameter is out U, so object must be valid covariantly. It is.

If it was declared as contravariant (in), then Ti must be valid contravariantly.

The first was declared in T, so string must be valid contravariantly. It is.

If it was declared as invariant, then Ti must be valid invariantly.

The third V was invariant, so int must be valid invariantly; it must be both valid contravariantly and covariantly. It is.

We pass all three checks; the type I<string, object, int> is valid covariantly.

OK, that one was easy.

Now let's look at a harder one.

interface IEnumerable<out W> { ... }
interface I<in T, out U, V> 
{
    IEnumerable<T> M();
}

IEnumerable<T> inside I is a type. Is IEnumerable<T> as used inside I valid covariantly?

Let's go through our definition. We have type argument T corresponding to type parameter out W. Note that T is a type parameter of I and a type argument of IEnumerable.

If the ith type parameter (W) was declared as covariant (out), then Ti (T) must be valid covariantly.

OK, so for IEnumerable<T> in I to be valid covariantly, T must be valid covariantly. Is it? NO. T was declared as in T. A type parameter that is declared in is never valid covariantly. Therefore the type IEnumerable<T> as used inside I is not valid covariantly, because the "must" condition is violated.

Again, like I said in my answer to your previous question, if "valid covariantly" and "valid contravariantly" are giving you grief, just give them different names. They are well-defined formal properties; you can call them anything you want if it makes it easier for you to understand.

No, your annotations are messed up.

That article is really hard to understand, in part because "valid covariantly" has nothing at all to do with covariance. Eric does point that out, but it means for every sentence you have to "unthink" the natural meaning, then think in terms of these weird definitions for "valid covariantly", "valid contravariantly", and "valid invariantly".

I strongly recommend you instead read about the Liskov Substitution Principle and then think about substitutability. Covariance, contravariance, and invariance have very simple definitions when looked at from the LSP perspective.

Then, you may notice that the C# rules at compile-time don't exactly match up with LSP (unfortunately -- and this is mainly a mistake made in Java and copied into C# to help court Java programmers). On the other hand, at runtime the LSP rules have to be followed, so if you start with those, you'll write code that both compiles and runs correctly, which I think is a more worthwhile endeavor than learning the C# language rules (unless you're writing a C# compiler).

how do you determine if a type argument that's declared as covariant (out) is covariantly valid?

Read rule 3.

in the closed constructed interface I{string, object int> of the generic interface I<in T, out U, V>, shouldn't the very fact that the type argument object is declared as a covariant type parameter out U in the generic interface declaration mean that the type argument object is covariant?

First, you're using "covariant" where you mean "covariantly valid". Remember, these are different things.

Second, let's go through it again. Is object covariantly valid? Yes, by rule 1. Is I<string, object, int> covariantly valid? Yes, by rule 3, which states that:

  • The type argument corresponding to T must be contravariantly valid.
  • The type argument corresponding to U must be covariantly valid.
  • The type argument corresponding to V must be both.

Since all three conditions are met, I<string, object, int> is covariantly valid.

In "An array type T[] where T is valid covariantly" what does the array element type T being valid covariantly mean?

I don't understand the question. We're defining what "covariantly valid" means. Rule 2 is part of the definition of "covariantly valid".

As an example, is object[] covariantly valid? Yes, because object is covariantly valid. If we had:

interface IFoo<out T> { T[] M(); }

Is T[] covariantly valid? Yes, because T is covariantly valid.

If we had

interface IBar<in T> { T[] M(); }

Is T[] covariantly valid? No. For an array type to be covariantly valid its element type must be covariantly valid, but T is not.

易学教程内所有资源均来自网络或用户发布的内容,如有违反法律规定的内容欢迎反馈
该文章没有解决你所遇到的问题?点击提问,说说你的问题,让更多的人一起探讨吧!