如何正确声明相关通用基 class 的未来子 class 类型的 return 类型?
How to properly declare a return type of a future subclass type of a related generic base class?
有人正在尝试使用 C# 在 ASP.NET MVC 中实现抽象。
他们有一个基础实体控制器,应该为其派生的 类(entities) 完成大部分工作。其中,他们有一个有问题的方法,该方法应该 return 每个 table 的数据库上下文(他们没有使用像 EF 这样的任何数据库框架)。方法如下:
protected abstract DbContext<EntityViewModel> CreateContext();
因此,假设他们有一个类别 table,应该实施该方法:
protected override DbContext<EntityViewModel> CreateContext()
{
return new CategoryDbContext();
}
但是 C# 说他们不能隐式转换它,等等...
上下文如下类:
public abstract class DbContext<T>
{
public abstract void Create(T entity);
public abstract List<T> Read(ModifyData data);
public abstract void Update(T entity);
public abstract void Delete(T entity);
}
public class CategoryDbContext : DbContext<CategoryViewModel>
{
public override void Create(CategoryViewModel entity)
{
}
public override List<CategoryViewModel> Read(ModifyData data)
{
}
public override void Update(CategoryViewModel entity)
{
}
public override void Delete(CategoryViewModel entity)
{
}
}
这个设计有什么问题,如何才能让这些 类 生效?
将基本实体控制器 class 修改为如下所示:
public abstract class BaseEntityController<TDbContext, TEntityViewModel>
where TDbContext : DbContext<TEntityViewModel>
where TEntityViewModel : EntityViewModel
{
protected abstract TDbContext CreateContext();
}
然后像这样子class BaseEntityController:
public class CategoryController : BaseEntityController<CategoryDbContext, CategoryViewModel>
{
protected abstract TDbContext CreateContext();
}
然后子class DbContext 如下:
public class CategoryDbContext : DbContext<CategoryViewModel>
{
public override void Create(CategoryViewModel entity)
{
}
public override List<CategoryViewModel> Read(ModifyData data)
{
}
public override void Update(CategoryViewModel entity)
{
}
public override void Delete(CategoryViewModel entity)
{
}
}
通过将 DbContext 本身的子 class 形式的泛型类型参数添加到 BaseEntityController class,我们能够 return 该占位符而不是基本泛型形式的 DbContext 从而避免转换问题并使代码更具强类型。
作为奖励,我们可以重构上面的代码,使用一些泛型嵌套 class (parametric/generic namespace) technique.例如:
public abstract class Entity<TEntity, TDbContext, TViewModel>
where TEntity : Entity<TEntity, TDbContext, TEntityViewModel>
where TDbContext : Entity<TEntity, TDbContext, TEntityViewModel>.BaseDbContext, new()
where TViewModel : Entity<TEntity, TDbContext, TEntityViewModel>.BaseViewModel
{
public class EntityController
{
protected TDbContext CreateContext() { return new TDbContext(); }
}
public abstract class BaseDbContext
{
public abstract void Create(TViewModel entity);
public abstract List<TViewModel> Read(ModifyData data);
public abstract void Update(TViewModel entity);
public abstract void Delete(TViewModel entity);
}
public abstract class BaseViewModel {}
}
然后子class(子命名空间?)通用命名空间及其成员如下:
public class Category : Entity<Category, Category.DbContext, Category.ViewModel>
{
pubic class DbContext : BaseDbContext
{
public override void Create(CategoryViewModel entity)
{
}
public override List<ViewModel> Read(ModifyData data)
{
}
public override void Update(CategoryViewModel entity)
{
}
public override void Delete(CategoryViewModel entity)
{
}
}
pubic class ViewModel : BaseViewModel
{
}
}
请注意,在没有其他要求的情况下,此时我们已经能够消除对 EntityController 进行子class 的需要。但是如果我们需要 subclass 它,我们可以将 EntityController 更改为 BaseEntityController,并可选择将其 subclass 形式的泛型类型参数添加到 Entity "generic namespace" 以备我们需要使用或 return 未来子 class 在基本代码中键入。
有人正在尝试使用 C# 在 ASP.NET MVC 中实现抽象。
他们有一个基础实体控制器,应该为其派生的 类(entities) 完成大部分工作。其中,他们有一个有问题的方法,该方法应该 return 每个 table 的数据库上下文(他们没有使用像 EF 这样的任何数据库框架)。方法如下:
protected abstract DbContext<EntityViewModel> CreateContext();
因此,假设他们有一个类别 table,应该实施该方法:
protected override DbContext<EntityViewModel> CreateContext()
{
return new CategoryDbContext();
}
但是 C# 说他们不能隐式转换它,等等...
上下文如下类:
public abstract class DbContext<T>
{
public abstract void Create(T entity);
public abstract List<T> Read(ModifyData data);
public abstract void Update(T entity);
public abstract void Delete(T entity);
}
public class CategoryDbContext : DbContext<CategoryViewModel>
{
public override void Create(CategoryViewModel entity)
{
}
public override List<CategoryViewModel> Read(ModifyData data)
{
}
public override void Update(CategoryViewModel entity)
{
}
public override void Delete(CategoryViewModel entity)
{
}
}
这个设计有什么问题,如何才能让这些 类 生效?
将基本实体控制器 class 修改为如下所示:
public abstract class BaseEntityController<TDbContext, TEntityViewModel>
where TDbContext : DbContext<TEntityViewModel>
where TEntityViewModel : EntityViewModel
{
protected abstract TDbContext CreateContext();
}
然后像这样子class BaseEntityController:
public class CategoryController : BaseEntityController<CategoryDbContext, CategoryViewModel>
{
protected abstract TDbContext CreateContext();
}
然后子class DbContext 如下:
public class CategoryDbContext : DbContext<CategoryViewModel>
{
public override void Create(CategoryViewModel entity)
{
}
public override List<CategoryViewModel> Read(ModifyData data)
{
}
public override void Update(CategoryViewModel entity)
{
}
public override void Delete(CategoryViewModel entity)
{
}
}
通过将 DbContext 本身的子 class 形式的泛型类型参数添加到 BaseEntityController class,我们能够 return 该占位符而不是基本泛型形式的 DbContext 从而避免转换问题并使代码更具强类型。
作为奖励,我们可以重构上面的代码,使用一些泛型嵌套 class (parametric/generic namespace) technique.例如:
public abstract class Entity<TEntity, TDbContext, TViewModel>
where TEntity : Entity<TEntity, TDbContext, TEntityViewModel>
where TDbContext : Entity<TEntity, TDbContext, TEntityViewModel>.BaseDbContext, new()
where TViewModel : Entity<TEntity, TDbContext, TEntityViewModel>.BaseViewModel
{
public class EntityController
{
protected TDbContext CreateContext() { return new TDbContext(); }
}
public abstract class BaseDbContext
{
public abstract void Create(TViewModel entity);
public abstract List<TViewModel> Read(ModifyData data);
public abstract void Update(TViewModel entity);
public abstract void Delete(TViewModel entity);
}
public abstract class BaseViewModel {}
}
然后子class(子命名空间?)通用命名空间及其成员如下:
public class Category : Entity<Category, Category.DbContext, Category.ViewModel>
{
pubic class DbContext : BaseDbContext
{
public override void Create(CategoryViewModel entity)
{
}
public override List<ViewModel> Read(ModifyData data)
{
}
public override void Update(CategoryViewModel entity)
{
}
public override void Delete(CategoryViewModel entity)
{
}
}
pubic class ViewModel : BaseViewModel
{
}
}
请注意,在没有其他要求的情况下,此时我们已经能够消除对 EntityController 进行子class 的需要。但是如果我们需要 subclass 它,我们可以将 EntityController 更改为 BaseEntityController,并可选择将其 subclass 形式的泛型类型参数添加到 Entity "generic namespace" 以备我们需要使用或 return 未来子 class 在基本代码中键入。