为何无法将CharacterProperty<T>转为CharacterProperty<object>?怎么解决?
为什么
CharacterProperty<Time>无法转换为CharacterProperty<object>?怎么解决? 这个问题核心是C#泛型的“不变性”导致的,我来给你拆解清楚原因和解决思路:
首先,哪怕Time是object的子类,CharacterProperty<Time>和CharacterProperty<object>在C#里是完全独立的类型——泛型类不会自动继承类型参数的继承关系。只有当泛型类型被显式声明为协变(用out关键字,仅允许类型参数作为输出/返回值)或逆变(用in关键字,仅允许类型参数作为输入/方法参数)时,这种转换才被允许。但你的CharacterProperty<T>同时包含了获取属性(输出T)和设置属性(输入T)的逻辑,没法单纯用协变或逆变接口解决,所以得换个方案。
解决方案:用非泛型基类/接口统一类型
我们可以通过定义非泛型的基类或接口,让所有泛型实现类都兼容这个统一类型,这样字典就能正常存储不同泛型实例了。
步骤1:定义非泛型的CharacterPropertyBase基类
创建抽象基类,把Set方法抽象为接受object参数的形式,让泛型子类处理类型转换:
public abstract class CharacterPropertyBase { public abstract void Set(Character c, object value); } public class CharacterProperty<T> : CharacterPropertyBase { private readonly Func<Character, T> _getter; private readonly Action<Character, T> _setter; public CharacterProperty(Func<Character, T> getter, Action<Character, T> setter) { _getter = getter; _setter = setter; } public override void Set(Character c, object value) { if (value is T typedValue) { _setter(c, typedValue); } else { throw new ArgumentException($"Value must be of type {typeof(T).Name}", nameof(value)); } } // 保留泛型Get方法(如果需要) public T Get(Character c) => _getter(c); }
步骤2:定义非泛型的IPropertyDistributor接口
给分布器也定义一个非泛型接口,让泛型分布器实现它:
public interface IPropertyDistributor { object Generate(); } public class NormalDistribution<T> : IPropertyDistributor { private readonly T _mean; private readonly T _stdDev; public NormalDistribution(T mean, T stdDev) { _mean = mean; _stdDev = stdDev; } public object Generate() { // 这里实现你的正态分布生成逻辑,返回T类型实例 // 比如针对Time类型的生成逻辑可以在这里处理 } }
步骤3:修改字典并添加元素
现在字典可以用非泛型类型作为键和值,添加元素时不会再有转换错误:
public Dictionary<CharacterPropertyBase, IPropertyDistributor> properties = new Dictionary<CharacterPropertyBase, IPropertyDistributor>(); // 添加元素的代码完全正常,无需强制转换 properties.Add( new CharacterProperty<Time>(c => c.age, (c, v) => c.age = v), new NormalDistribution<Time>(20 * Time.Y, 35 * Time.Y) );
步骤4:调整角色生成方法
生成角色的逻辑几乎不用改动,非泛型基类/接口已经统一了调用方式:
Character c = new Character(); foreach (var kvp in repartitor.properties) { object value = kvp.Value.Generate(); kvp.Key.Set(c, value); } return c;
额外说明
如果你的CharacterProperty<T>只有获取逻辑(没有设置),其实可以用协变接口(ICharacterProperty<out T>),但因为你同时需要设置属性(输入T),协变就不适用了——协变要求类型参数只能作为返回值,不能作为方法参数。所以用非泛型基类是最稳妥的方案,既能保留泛型的类型安全,又能统一字典的类型。
内容的提问来源于stack exchange,提问作者F. Morival
相关产品推荐
相关产品推荐

