能否将实例转换为以接口为类型参数的泛型接口?
问题背景
现有C#接口与类定义如下:
public interface ICutleryInfo { string Metal { get; set; } } public class KnifeInfo : ICutleryInfo { public string Metal { get; set; } public int NumberOfTeeth { get; set; } } public interface ICutlery<T> where T : ICutleryInfo { T CutleryInfo { get; set; } } public class Knife : ICutlery<KnifeInfo> { public KnifeInfo CutleryInfo { get; set; } public Knife() { CutleryInfo = new KnifeInfo(); } }
在编译时未知具体类型的场景下,通过Activator.CreateInstance()创建Knife实例后,尝试将其转换为ICutlery<ICutleryInfo>会触发无效转换异常,但转换为ICutlery<KnifeInfo>可正常运行。请问是否可实现目标转换?若可以,该如何操作?
解决方案
直接转换ICutlery<KnifeInfo>到ICutlery<ICutleryInfo>不可行,因为C#泛型接口默认是不变的。你的ICutlery<T>接口中CutleryInfo属性同时包含get和set方法,若允许这种跨类型转换会破坏类型安全——比如你可以尝试将ForkInfo(假设它也实现ICutleryInfo)赋值给ICutlery<ICutleryInfo>.CutleryInfo,但底层实际是KnifeInfo类型,会导致类型错误。
以下是三种可行的实现方式:
方案一:拆分协变只读接口(仅需读取场景)
如果你的业务场景只需要读取CutleryInfo而不需要修改,可以定义一个带out关键字的协变只读接口,利用泛型协变特性实现向上转换:
// 协变只读接口,out关键字表示T支持协变 public interface ICutleryReadOnly<out T> where T : ICutleryInfo { T CutleryInfo { get; } } // 原泛型接口继承只读接口 public interface ICutlery<T> : ICutleryReadOnly<T> where T : ICutleryInfo { new T CutleryInfo { get; set; } } // Knife类无需额外修改,自动实现两个接口 public class Knife : ICutlery<KnifeInfo> { public KnifeInfo CutleryInfo { get; set; } public Knife() { CutleryInfo = new KnifeInfo(); } }
使用示例:
object instance = Activator.CreateInstance(typeof(Knife)); // 转换为协变只读接口 var cutlery = (ICutleryReadOnly<ICutleryInfo>)instance; ICutleryInfo info = cutlery.CutleryInfo; // 正常读取属性
方案二:定义非泛型基接口(需读写场景)
如果需要支持读写操作,可以定义一个非泛型的ICutlery基接口,让泛型接口继承它,并在实现类中显式处理类型转换:
// 非泛型基接口 public interface ICutlery { ICutleryInfo CutleryInfo { get; set; } } // 泛型接口继承非泛型接口 public interface ICutlery<T> : ICutlery where T : ICutleryInfo { new T CutleryInfo { get; set; } } public class Knife : ICutlery<KnifeInfo> { public KnifeInfo CutleryInfo { get; set; } // 显式实现非泛型接口,确保类型安全 ICutleryInfo ICutlery.CutleryInfo { get => CutleryInfo; set { if (value is KnifeInfo knifeInfo) CutleryInfo = knifeInfo; else throw new ArgumentException("必须赋值KnifeInfo类型", nameof(value)); } } public Knife() { CutleryInfo = new KnifeInfo(); } }
使用示例:
object instance = Activator.CreateInstance(typeof(Knife)); // 转换为非泛型接口 var cutlery = (ICutlery)instance; ICutleryInfo info = cutlery.CutleryInfo; // 正常读取 cutlery.CutleryInfo = new KnifeInfo(); // 正常写入(传入非KnifeInfo会抛出异常)
方案三:反射操作(无需修改现有代码)
如果无法修改现有接口和类的定义,可以通过反射直接操作CutleryInfo属性:
object instance = Activator.CreateInstance(typeof(Knife)); // 获取CutleryInfo属性 PropertyInfo infoProp = instance.GetType().GetProperty("CutleryInfo"); // 读取属性值并转换为ICutleryInfo ICutleryInfo info = (ICutleryInfo)infoProp.GetValue(instance); // 写入属性(需确保传入类型正确) infoProp.SetValue(instance, new KnifeInfo());
内容的提问来源于stack exchange,提问作者komodosp
相关产品推荐
相关产品推荐

