【问题标题】:How to properly declare a return type of a future subclass type of a related generic base class?如何正确声明相关泛型基类的未来子类类型的返回类型?
【发布时间】:2015-07-24 16:25:41
【问题描述】:

有人试图用 C# 在 ASP.NET MVC 中实现抽象。

他们有一个基本实体控制器,它应该为其派生类(实体)完成大部分工作。在其中,他们有一个有问题的方法,该方法应该返回每个表的数据库上下文(他们没有使用任何数据库框架,如 EF)。方法如下:

protected abstract DbContext<EntityViewModel> CreateContext();

所以,假设他们有一个 Category 表,应该实现该方法:

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)
    {
    }
}

此设计有什么问题?如何更改这些类才能正常工作?

【问题讨论】:

  • 如果问题被标记为重复,为什么要重新发布?另一个问题是什么?
  • @AlexFoxGill 对于那些可以看到的人stackoverflow.com/questions/31605068/… 我投票支持取消删除
  • 另一个问题的答案基本上回答了为什么原始发布者无法将 CategoryDbContext 转换为 DbContext。它没有回答原始帖子中的以下陈述和问题:“我正在尝试使用 C# 在 ASP.NET MVC 中实现抽象......我在这里做错了什么?”
  • @TyreeJackson 删除元内容,即“(w/o perm)”、“问题被标记为 dup”等。对于阅读问题的人来说,是否无关紧要它基于另一个问题,只要它有用。其他人投了反对票,因为他们不明白你为什么这样做。所以把问题写成你的问题,最后你可以提供一个链接给想到它的用户。
  • @Armfoot 我已根据您的反馈更新了问题。再次感谢!您在这里和 SO Meta 上的回复对像我这样的 SO 贡献者新手非常有帮助。

标签: c# generics generic-programming parametric-namespaces


【解决方案1】:

将基本实体控制器类修改为如下所示:

public abstract class BaseEntityController<TDbContext, TEntityViewModel>
    where TDbContext : DbContext<TEntityViewModel>
    where TEntityViewModel : EntityViewModel
{
    protected abstract TDbContext CreateContext();
}

然后像这样子类化 BaseEntityController:

public class CategoryController : BaseEntityController<CategoryDbContext, CategoryViewModel>
{
    protected abstract TDbContext CreateContext();
}

然后像这样子类化 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 本身的子类形式的泛型类型参数添加到 BaseEntityController 类,我们能够返回该占位符而不是 DbContext 的基本泛型形式,从而避免强制转换问题并使代码具有更强的类型。

作为奖励,我们可以重构上面的代码,使用一些泛型嵌套类(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 {}

}

然后子类(子命名空间?)通用命名空间及其成员如下:

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 的子类化的需要。但是如果我们需要对其进行子类化,我们可以将 EntityController 更改为 BaseEntityController,并且可以选择将其子类形式的泛型类型参数添加到实体“通用命名空间”中,以防我们需要在基础代码中使用或返回该未来子类类型。

【讨论】:

  • 它正在准备复制粘贴:)
  • @Jashaszun 这是过去编写 sql 语句时常用的一种旧制表位格式化技术。它旨在与 Visual Studio 或其他支持代码折叠的类似编辑器等工具中的折叠方法一起使用。 Google 弹性制表位。
  • @TyreeJackson 好的。虽然这是有道理的,但我认为将代码正常格式化会更好(即关键字之间有一个空格等)。
  • @DavidL 感谢您的牺牲。
  • @DavidL 然后你必须学会​​更聪明地工作,而不是更努力地工作(除非你按小时计酬):-) Notepad++,使用正则表达式搜索和替换,([A-Za-z&gt;]) +(@ 前两个空格987654328@) 到 \1 1 后面的一个空格)
猜你喜欢
  • 1970-01-01
  • 1970-01-01
  • 1970-01-01
  • 1970-01-01
  • 2014-11-25
  • 1970-01-01
  • 1970-01-01
  • 2018-12-14
  • 1970-01-01
相关资源
最近更新 更多