为什么 C# 11 编译器拒绝接受作为同一类型赋值源的泛型参数?

Rod*_*ave 3 .net c# generics

在尝试让 C# 编译器完成尽可能多的工作时,我通常最终会使用(有些人可能会说滥用)通用性。

我经常发现一种特殊情况,但我无法解释原因。据我所知,如果有一个类似于Eric Lippert对这个类似问题的精彩答案的解释,那就太好了:https: //stackoverflow.com/a/17440148/257372

我已经调整了真实类的名称以使用 Animal ,以便它与上面的答案相匹配。我还删除了所有方法和任何其他不必要的细节,以使事情尽可能简单。

public interface IAnimal { }

public interface IAnimalOperationResult<out TAnimal> where TAnimal : IAnimal { }

public record DefaultSuccessfulResult<TAnimal>() : IAnimalOperationResult<TAnimal> where TAnimal : IAnimal;

public abstract class AnimalHandler<TAnimal, TSuccessfulAnimalOperationResult> where TAnimal : IAnimal
    where TSuccessfulAnimalOperationResult : IAnimalOperationResult<IAnimal> { }

// The compiler complains here with the following message:
// Error CS0311: The type 'DefaultSuccessfulResult<TAnimal>' cannot be used as type parameter 'TSuccessfulAnimalOperationResult' in the generic type or method 'AnimalHandler<TAnimal, TSuccessfulAnimalOperationResult>'.
// There is no implicit reference conversion from 'DefaultSuccessfulResult<TAnimal>' to 'IAnimalOperationResult<IAnimal>'
public class AnimalHandlerWithDefaultSuccessfulResult<TAnimal> : AnimalHandler<TAnimal, DefaultSuccessfulResult<TAnimal>>
    where TAnimal : IAnimal { }
Run Code Online (Sandbox Code Playgroud)

错误消息显示没有从“ DefaultSuccessfulResult<TAnimal> ”到“ IAnimalOperationResult<IAnimal> ”的隐式引用转换

根据编译器的说法,这是不正确的,因为它接受以下代码:

public record Dog() : IAnimal;

[Fact]
public void CanAssignValues()
{
    DefaultSuccessfulResult<Dog> source = new();

    // This assignment requires the same implicit reference conversion the compiler claims doesn't exist.
    // However, in this instance, the compiler accepts it.
    IAnimalOperationResult<IAnimal> target = source;
}
Run Code Online (Sandbox Code Playgroud)

我显然错过了一些东西,但是什么呢?

Gur*_*ron 6

简而言之 - 添加class通用约束:

public class AnimalHandlerWithDefaultSuccessfulResult<TAnimal> : AnimalHandler<TAnimal, DefaultSuccessfulResult<TAnimal>>
    where TAnimal : class, IAnimal { }
Run Code Online (Sandbox Code Playgroud)

原因是 C# 中的差异仅支持引用类型。来自关于泛型方差的文档

方差仅适用于引用类型;如果为变体类型参数指定值类型,则该类型参数对于生成的构造类型是不变的。

您将能够通过将Dog记录更改为值类型(使用 C# 10 的记录结构)来“确认”这种情况:

public record struct Dog() : IAnimal; 

DefaultSuccessfulResult<Dog> source = new();
// Following produces compiler error:
// Cannot implicitly convert type 'DefaultSuccessfulResult<Dog>' to 'IAnimalOperationResult<IAnimal>'. 
// An explicit conversion exists (are you missing a cast?)
IAnimalOperationResult<IAnimal> target = source;
Run Code Online (Sandbox Code Playgroud)