泛型中受接口约束的对象生成遇协变/逆变问题:无法隐式转换类型
问题
我正在构建一个代码生成器,该生成器将生成遵循继承自基础接口的接口的代码。原本打算使用泛型类来实现,但遇到了协变/逆变问题,正尝试找出最佳解决方案。不确定是否可以通过协变/逆变来解决,还是必须放弃泛型。
需求简化后的示例代码如下:
public interface Animal { string Name { get; } } public class Dog : Animal { public string Name { get; set; } public bool WalksEveryDay { get; set; } } public class Snake : Animal { public string Name { get; set; } public bool GoesForSlitherEveryDay { get; set; } } public class AnimalInterfaceFactory<T> where T : Animal { public T GetNew(int animaltype) { switch(animaltype) { case 0: return new Dog(); // 编译错误 - 无法将类型Dog隐式转换为T case 1: return new Snake(); // 编译错误 - 无法将类型Snake隐式转换为T // 还需默认分支 } } }
我希望避免为每个可能的类型编写如下重复代码,因为代码生成器需要支持的类型很多:
public class DogFactory { public Animal GetNew() { return new Dog(); // 此代码可行,因为Dog继承自Animal,且该工厂不会用于Snake } }
解决方案
1. 显式类型转换+运行时校验
编译器无法推断泛型参数T与具体动物类型的关系,因此可以通过显式转换解决编译错误,同时增加运行时校验避免类型不匹配的异常:
public class AnimalInterfaceFactory<T> where T : Animal { public T GetNew(int animaltype) { Animal animal = animaltype switch { 0 => new Dog(), 1 => new Snake(), _ => throw new ArgumentOutOfRangeException(nameof(animaltype), "无效的动物类型") }; if (animal is not T targetType) { throw new InvalidCastException($"无法将{animal.GetType().Name}转换为{T.Name}"); } return targetType; } }
使用时需保证传入的T与animaltype匹配,例如new AnimalInterfaceFactory<Dog>().GetNew(0)是合法调用。
2. 放弃泛型,直接返回基接口
如果不需要严格返回泛型类型,直接返回Animal基接口会让工厂类更简洁,也避免了泛型转换问题:
public class AnimalInterfaceFactory { public Animal GetNew(int animaltype) { return animaltype switch { 0 => new Dog(), 1 => new Snake(), _ => throw new ArgumentOutOfRangeException(nameof(animaltype), "无效的动物类型") }; } }
调用方可以通过模式匹配或显式转换处理具体类型:
var factory = new AnimalInterfaceFactory(); var animal = factory.GetNew(0); if (animal is Dog dog) { // 处理Dog专属逻辑 }
3. 泛型方法+类型映射字典
如果想保留泛型返回同时避免重复工厂类,可以用泛型方法+字典映射的方式,这种方案更适配代码生成器的批量扩展需求:
public class AnimalInterfaceFactory { // 提前注册所有支持的动物类型创建逻辑 private static readonly Dictionary<Type, Func<Animal>> _typeCreators = new() { { typeof(Dog), () => new Dog() }, { typeof(Snake), () => new Snake() } }; public T GetNew<T>() where T : Animal { if (_typeCreators.TryGetValue(typeof(T), out var creator)) { return (T)creator(); } throw new NotSupportedException($"不支持创建类型{T.Name}"); } }
调用时直接指定目标类型即可:
var factory = new AnimalInterfaceFactory(); Dog dog = factory.GetNew<Dog>(); Snake snake = factory.GetNew<Snake>();
新增类型时只需在字典中添加对应的类型和创建委托,无需修改核心逻辑,非常适合代码生成器自动生成。
内容的提问来源于stack exchange,提问作者Tom22
相关产品推荐
相关产品推荐

