You need to enable JavaScript to run this app.
优惠活动
大模型
产品
解决方案
定价
更多

C#如何在父类中传入子类作为泛型类型

问题

开发中使用某第三方库,该库提供的类要求将实例化它的当前类作为泛型参数传入。现有继承自DbContext的BaseContext类,其OnModelCreating方法中实例化了SqliteDropCreateDatabaseWhenModelChanges<BaseContext>并设置为数据库初始化器。

现在希望继承BaseContext类,但不想重写OnModelCreating方法,却不知如何让父类中的泛型参数自动使用子类类型。尝试用typeof(this)作为泛型参数,此方法无效。

示例代码:

public class BaseContext : DbContext
{
    public BaseContext(string connectionString) : base(connectionstring)
    {
    }

    protected override void OnModelCreating(DbModelBuilder modelBuilder)
    {
        // 此处
        var sqliteConnectionInitializer = new SqliteDropCreateDatabaseWhenModelChanges<BaseContext>(modelBuilder);
        Database.SetInitializer(sqliteConnectionInitializer);
    }
}

尝试的无效代码:

var sqliteConnectionInitializer = new SqliteDropCreateDatabaseWhenModelChanges<typeof(this)>(modelBuilder);
解决方案

方案一:使用奇异递归模板模式(CRTP)

这是最简便且性能最优的编译时解决方案,通过泛型基类让子类自动传入自身类型。

修改后的基类代码:

public class BaseContext<TContext> : DbContext 
    where TContext : BaseContext<TContext>
{
    public BaseContext(string connectionString) : base(connectionString)
    {
    }

    protected override void OnModelCreating(DbModelBuilder modelBuilder)
    {
        var sqliteConnectionInitializer = new SqliteDropCreateDatabaseWhenModelChanges<TContext>(modelBuilder);
        Database.SetInitializer(sqliteConnectionInitializer);
    }
}

子类继承时只需将自身作为泛型参数传入:

public class MyDerivedContext : BaseContext<MyDerivedContext>
{
    public MyDerivedContext(string connectionString) : base(connectionString)
    {
    }
    // 无需重写OnModelCreating方法
}

原理:通过泛型约束where TContext : BaseContext<TContext>确保传入的类型是当前基类的子类,编译时TContext会被替换为子类的实际类型,从而让父类自动使用子类作为泛型参数初始化第三方库类。

方案二:反射动态实例化(无需修改基类泛型结构)

如果无法修改基类的泛型定义,可以通过反射在运行时动态获取当前实例的子类类型:

protected override void OnModelCreating(DbModelBuilder modelBuilder)
{
    // 获取当前实例的实际类型(子类类型)
    var contextType = this.GetType();
    // 构造泛型版本的初始化器类型
    var initializerGenericType = typeof(SqliteDropCreateDatabaseWhenModelChanges<>).MakeGenericType(contextType);
    // 动态创建实例
    var initializer = Activator.CreateInstance(initializerGenericType, modelBuilder);
    // 设置数据库初始化器
    Database.SetInitializer(initializer as IDatabaseInitializer<DbContext>);
}

此方案无需调整基类结构,但因为是运行时反射操作,性能略逊于编译时确定的泛型方案,适合无法修改基类的场景。


内容的提问来源于stack exchange,提问作者emilaz

相关产品推荐
方舟 Agent Plan

超全模态模型 × Harness 升级,最新支持 Deepseek-V4.1-Flash、GLM-5.3 系列、Doubao-Seedream-5.0-pro、Kimi-K3 (部分), 限时 9.9 元起

最近更新时间:2026.07.29 09:07:29