You need to enable JavaScript to run this app.
优惠活动
大模型
产品
解决方案
定价
更多

C#协变(out)与逆变(in)关键字使用示例及不使用的影响咨询

Understanding the Impact of Omitting out/in and Practical Examples in C#

Hey there! Since you already grasp the core ideas of covariance and contravariance, let's dive straight into what goes wrong when you skip the out and in keywords, followed by clear examples of how they fix those issues.

What Happens Without out (Covariance) and in (Contravariance)?

C# doesn't allow implicit variant conversions on generic interfaces/delegates by default—you need to explicitly mark them with out or in to enable that behavior. Here's what breaks:

Covariance Issue (No out)

Suppose you have a simple class hierarchy:

public class Animal { }
public class Dog : Animal { }

And a generic interface without the out modifier:

public interface IRepository<T> {
    T GetById(int id);
}

public class DogRepository : IRepository<Dog> {
    public Dog GetById(int id) => new Dog();
}

If you try to assign a DogRepository to an IRepository<Animal> variable:

IRepository<Animal> animalRepo = new DogRepository(); // Compile error!

You'll hit a compile error like this:

Cannot implicitly convert type 'DogRepository' to 'IRepository'. An explicit conversion exists (are you missing a cast?)

Even though Dog inherits from Animal, the compiler has no way of knowing it's safe to treat IRepository<Dog> as IRepository<Animal>—without out, it assumes T could be used as an input type (like a method parameter), which would break type safety.

Contravariance Issue (No in)

For contravariance, let's look at a validator interface without the in modifier:

public interface IValidator<T> {
    bool Validate(T entity);
}

public class AnimalValidator : IValidator<Animal> {
    public bool Validate(Animal entity) => true; // Validates any Animal subtype
}

If you try to use this validator for Dog instances:

IValidator<Dog> dogValidator = new AnimalValidator(); // Compile error!

Another compile error pops up:

Cannot implicitly convert type 'AnimalValidator' to 'IValidator'. An explicit conversion exists (are you missing a cast?)

Even though an AnimalValidator can handle a Dog (since Dog is an Animal), without in, the compiler doesn't recognize that this reverse conversion is safe.

Using out (Covariance) and in (Contravariance) – Practical Examples

Covariance with out

Mark your interface with out to indicate T is only used as a return type (output), making the interface covariant:

public interface IReadOnlyRepository<out T> {
    T GetById(int id);
    // Note: You can't define methods with T as a parameter here—out restricts T to output positions
}

public class DogRepository : IReadOnlyRepository<Dog> {
    public Dog GetById(int id) => new Dog();
}

Now this assignment works flawlessly:

IReadOnlyRepository<Animal> animalRepo = new DogRepository(); // No error!
Animal myPet = animalRepo.GetById(1); // Returns a Dog, which safely upcasts to Animal

The out keyword tells the compiler: "T is only ever returned from this interface, so substituting a derived type with a base type won't break anything."

Contravariance with in

Mark your interface with in to indicate T is only used as an input type (method parameters), making it contravariant:

public interface IValidator<in T> {
    bool Validate(T entity);
    // Note: You can't define methods returning T here—in restricts T to input positions
}

public class AnimalValidator : IValidator<Animal> {
    public bool Validate(Animal entity) {
        // Logic that works for any Animal (including Dog)
        return entity != null;
    }
}

Now you can safely use the AnimalValidator for Dog instances:

IValidator<Dog> dogValidator = new AnimalValidator(); // No error!
bool isDogValid = dogValidator.Validate(new Dog()); // Works because Dog is an Animal

The in keyword tells the compiler: "T is only ever passed into this interface, so substituting a base type with a derived type is safe."

Bonus: Delegate Examples

Delegates also support covariance and contravariance with out/in:

  • Covariant delegate (using out):
    public delegate T AnimalFactory<out T>();
    
    public Dog CreateDog() => new Dog();
    
    AnimalFactory<Animal> animalFactory = CreateDog; // Works thanks to covariance
    
  • Contravariant delegate (using in):
    public delegate bool AnimalChecker<in T>(T animal);
    
    public bool CheckAnimalHealth(Animal animal) => true;
    
    AnimalChecker<Dog> dogChecker = CheckAnimalHealth; // Works thanks to contravariance
    

内容的提问来源于stack exchange,提问作者Hesan Mahdi

相关产品推荐
方舟 Agent Plan

超全模态模型 × Harness 升级,最新支持 Deepseek-V4.1-Flash、GLM-5.3 系列、Doubao-Seedream-5.0-pro、Kimi-K3 (部分), 限时 9.9 元起

最近更新时间:2026.05.06 06:48:07