1

I've tried looking up more specifics on this online but haven't found much info on this topic. I'm reading about generic interfaces from the C# documentation and everything seems reasonable until I get to this statement.

Generic Interfaces (C# Programming Guide) (June 13, 2025 snapshot here):

Generic interfaces can inherit from non-generic interfaces if the generic interface is covariant, which means it only uses its type parameter as a return value. In the .NET class library, IEnumerable<T> inherits from IEnumerable because IEnumerable<T> only uses T in the return value of GetEnumerator and in the Current property getter.

My breakdown of the highlighted sentence is that I can do :

IGeneriInterface<T> : INormalInterface

Only when T is used as a return type for methods, but never a parameter type. But the compiler lets me do this.

So I'm lost if this is outdated info or if I misunderstood it (and most likely I did). If anyone could write out what inheritance is not allowed by C# in relation to this that would be great, and if this also applies to class inheritance and so on.

2
  • See "Variance in Generic Interfaces (C#)" Commented Jul 5 at 1:43
  • 1
    I implemented variance in the C# compiler. That sentence is nonsensical and should be removed from the docs. Lots of poorly reviewed stuff has crept into the official documentation unfortunately. Commented Aug 17 at 23:21

2 Answers 2

5

The statement "Generic interfaces can inherit from non-generic interfaces if the generic interface is covariant, which means it only uses its type parameter as a return value." is obviously bogus. You can easily prove this yourself by trying to define and implement a generic interface which is not covariant and which inherits from a non-generic interface:

public interface INonGeneric
{
    void PassValue(string value);
    string ReturnValue();
    string Prop { get; }
}

public interface INotCovariant<T> : INonGeneric
{
    void PassValue(T value);
    new T ReturnValue();
    new T Prop { get; set; }
}


public class Foo : INotCovariant<string>
{
    public string Prop { get => _p; set => _p = value; }
    public void PassValue(string value) => _p = value;
    public string ReturnValue() => _p;

    private string _p = "";
}

public class Bar : INotCovariant<int>
{
    public int Prop { get => _i; set => _i = value; }
    string INonGeneric.Prop => _s;
    public void PassValue(int value) => _i = value;
    public void PassValue(string value) => _s = value;
    public int ReturnValue() => _i;
    string INonGeneric.ReturnValue() => _s;

    private string _s = "";
    private int _i;
}

This is proper legal C# and it just works...

Also, an example from .NET's own class library: System.Buffers.IMemoryOwner<T> - a generic interface which is not covariant and which inherits from a non-generic interface (IDisposable).

And the reason for IEnumerable<T> being covariant is entirely unrelated to it inheriting from the non-generic IEnumerable interface.

I don't know what circumstances led to this statement appearing in the official documentation. For what's worth, i opened a documentation issue regarding this: https://github.com/dotnet/docs/issues/47104. I am unable to tell when and how this would be addressed by the team (or contributors), but considering that there are about 500 untriaged documentation issue reports as of the time i am writing this i doubt it will be anytime soon...

Sign up to request clarification or add additional context in comments.

5 Comments

I suspect from the change history that the statements in question were not updated when .Net Framework 4 was released.
Remember that .NET had generics since Framework 2.0 without variance until Framework 4. That statement therefore could not be like that before Framework 4 was released, simply because there was no basis to talk about variance in .NET/C# prior to that. The soonest this statement could appear in the documentation was with the introduction of variance in Framework 4. By the way, the change history reveals that some edits were made to this statement, but nobody did seem to notice the statement being entirely wrong, apparently. Beats me, i have no clue how this got into the docs.
Correct, statement seems to be quite far fetched and can be easily disproved :)
Agree the doc paragraph is bogus. What they might have been trying to say is, Don't create interface hierarchies that compile successfully but whose implementations are required to throw in runtime, because a non-generic base interface method takes some object parameter which the generic interface refines (via new) to be of some generic type. MSFT did this with their class hierarchies (e.g. List<T> implements IList so IList.Add() could throw) but by and large seems to avoided doing it with their interface hierarchies.
Thank you so much for the in depth answer. Would have gone on with this at the back of my head for a long time, given it was from the actual C# documentation. Hope this clears it up for anyone else if they run into this issue and Google it.
0

I think the meaning of this sentence is not clear enough, but it should not be considered obviously bogus. It does not mean that ONLY covariant interfaces can inherit non-generic interfaces.

I have done some archaeological work, and the earliest version of this sentence I can find is on Jan 30, 2006. The original text is:

Generic interfaces can inherit from non-generic interfaces if the generic interface is contra-variant, which means it only uses its type parameter as a return value. In the .NET Framework class library, IEnumerable<T> inherits from IEnumerable because IEnumerable<T> only uses T in the return value of GetEnumerator and in the Current property getter.

It can be seen that there have been no changes except for the "contra-variant" fix. The author of this sentence is most likely Brad Abrams, as the same mistake appeared in his blog post 'Why does IEnumerable inherits from IEnumerable':

As it turns out, the only generic interface for which this is possible is IEnumerable<T>, because only IEnumerable<T> is contra-variant: In IEnumerable<T>, the type parameter T is used only in "output" positions (return values) and not in "input" positions (parameters).

Returning to the sentence, it is obvious that we know regardless of whether an interface is covariant, contravariant, or invariant, it can inherit from another interface.

However, let's focus on the context, the previous sentence is:

The rules of inheritance that apply to classes also apply to interfaces:

So this paragraph first clarifies the general interface inheritance rules, and then describes a specific inheritance rule. Therefore, the problematic statement does not negate other inheritance rules in the C# language.

The problematic statement is about the relationship between IEnumerable<T> and IEnumerable. I think we should not consider generic interfaces and non-generic interfaces as arbitrary interfaces. The definition of non-generic interfaces here should be the counterpart of generic interfaces, that is, I <-> I<object>. This is also mentioned in the post above:

Ideally all of the generic collection interfaces (e.g. ICollection<T>, IList<T>) would inherit from their non-generic counterparts such that generic interface instances could be used both with generic and non-generic code.

Also we should not consider "the generic interface is covariant" as a necessary condition for inheritance. Please pay attention to the following sentence in the same article:

Generic classes can implement generic interfaces or closed constructed interfaces as long as the class parameter list supplies all arguments required by the interface

There is a difference in the verbalism compared to the problematic statement in terms of relevance. In other words, the sentence can be expressed as: Generic interfaces are not required to inherit from their non-generic counterparts if the generic interface is not covariant.

Regarding this point, Eric Lippert explained as follows:

BONUS QUESTION: Why does IEnumerable<T> inherit from IEnumerable but IList<T> does not inherit from IList?

A sequence of integers can be treated as a sequence of objects, by boxing every integer as it comes out of the sequence. But a read-write list of integers cannot be treated as a read-write list of objects, because you can put a string into a read-write list of objects. An IList<T> is not required to fulfill the whole contract of IList, so it does not inherit from it.

Finally, I checked that all covariant interfaces in the .NET library (IEnumerable, IEnumerator, IQueryable, IOrderedQueryable) inherit their non-generic counterparts, while none of the non-covariant interfaces do so. So I think describes is not a language specification, but a design rule.

5 Comments

The apparent connection to the blog post is especially interesting. It seems the origin of that statement and the lost intention behind it was more or less an explanation of why the generic collection interfaces do not inherit their non-generic counterparts. Still, it's kinda wild how this got mangled into the statement that survived for so many years in the programming guide. (The statement as written is bogus. That's not a matter of being not precise enough, it is obviously and demonstrably a false statement -- hence bogus.)
No, you can't say it is a false statement. It is in a simple "if P then Q" form. For example, if 2 numbers are positive, they can be added. It doesn't cover all addition, but it is not a false statement.
C# would technically allow IList<T> : IList, the problem is that your implementation of IList would be breaking the contract of that interface by throwing runtime exceptions. Is there some other context missing here? Was there an early draft version of the compiler / runtime where IEnumerable.GetEnumerator() and IEnumerable<T>.GetEnumerator() were the same implementation at runtime? And therefore the statement was true at the time?
I don't think there is such a draft because this article should have been written after the release of C# 2. And if you think about it again, is this statement still true now? It states that IEnumerable<T> can inherit IEnumerable, but it does not deny IList<T> : IList, as the latter is legal but unwise, so there is no need to specifically state it.
I think it's clear to me now, that this particular statement is about how you transition from older non-generic interfaces. "Generic interfaces [should only] inherit from non-generic interfaces [that provide the same behaviour via object] if the generic interface is covariant". If you are learning how to write modern C#, you would try to avoid creating the non-generic interface at all.

Your Answer

By clicking “Post Your Answer”, you agree to our terms of service and acknowledge you have read our privacy policy.

Start asking to get answers

Find the answer to your question by asking.

Ask question

Explore related questions

See similar questions with these tags.