C#泛型基类场景下,如何获取可兼容子类的通用父类?
场景与示例代码
我刚开始尝试使用带泛型类型参数的基类,示例代码如下:
public class BaseClass<T> : where T : new() { } public class ChildClassA : BaseClass<A> { } public class ChildClassB : BaseClass<B> { } public class A { } public class B { }
我知道可以像这样实例化:BaseClass<A> a = new ChildClassA() 和 BaseClass<B> b = new ChildClassB()。但我想找一个通用父类,能在未知泛型类型的情况下接收BaseClass的任意子类实例。
尝试的方法与报错
我试过用BaseClass<object> a = new ChildClassA(),但出现编译错误:
Cannot implicitly convert type 'A' to type 'object'
请问C#中有没有其他可用的通用父类,还是这个需求根本无法实现?
更新:实际业务场景
我在做一个WinUI项目,多个ViewModel通过BaseClass实现了新增、编辑、记录导航等共享方法。我希望把这些ViewModel传递给一个Toolbar用户控件,这样就能在XAML中把Toolbar的Click事件绑定到ViewModel的方法,不用每次实现Toolbar都重复传递10个相同的函数。
当前Toolbar用户控件的代码:
public sealed partial class Toolbar : UserControl { public ViewModelBase_Toolbar<object> ViewModel { get => (ViewModelBase_Toolbar<object>)GetValue(ViewModelProperty); set => SetValue(ViewModelProperty, value); } public static readonly DependencyProperty ViewModelProperty = DependencyProperty.Register(nameof(ViewModel), typeof(ViewModelBase_Toolbar<object>), typeof(Toolbar), new PropertyMetadata(null)); public Toolbar() { this.InitializeComponent(); } }
解决方案
C#中泛型默认是不变量,也就是说BaseClass<A>和BaseClass<object>之间不存在继承关系,因此无法直接转换。要解决这个问题,有两种常用思路:
1. 定义非泛型的基类/接口(推荐)
给泛型基类添加一个非泛型的父类或接口,把所有Toolbar需要调用的共享方法(比如新增、编辑、导航)都定义在这个非泛型类型里。
示例代码:
// 非泛型基类,封装Toolbar需要的所有共享方法 public abstract class ViewModelBase_Toolbar { public abstract void Add(); public abstract void Edit(); public abstract void Navigate(); // 其他需要绑定的方法... } // 泛型基类继承自非泛型基类 public class BaseClass<T> : ViewModelBase_Toolbar where T : new() { public override void Add() { // 泛型相关的实现逻辑 var newItem = new T(); // ... } public override void Edit() { // 编辑逻辑实现 } public override void Navigate() { // 导航逻辑实现 } } // 子类保持原有定义不变 public class ChildClassA : BaseClass<A> { } public class ChildClassB : BaseClass<B> { } public class A { } public class B { }
然后修改Toolbar的依赖属性类型为非泛型基类:
public sealed partial class Toolbar : UserControl { public ViewModelBase_Toolbar ViewModel { get => (ViewModelBase_Toolbar)GetValue(ViewModelProperty); set => SetValue(ViewModelProperty, value); } public static readonly DependencyProperty ViewModelProperty = DependencyProperty.Register( nameof(ViewModel), typeof(ViewModelBase_Toolbar), typeof(Toolbar), new PropertyMetadata(null)); public Toolbar() { this.InitializeComponent(); } }
这样所有继承自BaseClass<T>的ViewModel都能赋值给Toolbar的ViewModel属性,XAML中绑定方法也能正常工作。
2. 使用泛型协变(仅适用于特定场景)
如果共享方法只涉及输出T(比如返回T类型实例),可以定义协变接口(用out关键字标记T):
// 协变接口,out表示T是协变类型 public interface IViewModelBase_Toolbar<out T> where T : new() { T GetNewItem(); void Navigate(); // 注意:协变接口不能包含接收T作为参数的方法(比如void Add(T item)) } public class BaseClass<T> : IViewModelBase_Toolbar<T> where T : new() { public T GetNewItem() => new T(); public void Navigate() { // 导航逻辑实现 } }
此时Toolbar的属性可以定义为IViewModelBase_Toolbar<object>,但这种方式限制很大,因为协变不允许接口中有输入T的方法,所以仅适用于只读类场景。你的业务场景涉及新增、编辑等操作,显然第一种方案更合适。
总结
直接用BaseClass<object>接收ChildClassA实例是不可行的,这是由泛型不变性决定的。最实用的解决方案是定义一个非泛型的基类/接口,封装所有需要在Toolbar中调用的共享方法,让泛型基类继承它,这样就能统一处理所有子类实例。
内容的提问来源于stack exchange,提问作者Bloody Solomon

