【问题标题】:override methods of generic base type覆盖泛型基类型的方法
【发布时间】:2020-03-14 16:49:12
【问题描述】:

我目前的结构是这样的:

public class CustomerRepository
{
    // ..

    public virtual Customer LoadCustomer()
    {
        // ...
    }
}

public class OracleCustomerRepository : CustomerRepository
{
    private OracleConnection oracleConnection;

    public OracleCustomerRepository()
    : base(){
        if(this.EnsureIsConnected()){
             // ...
        }
    }

    private bool EnsureIsConnected(){
        // ..
    }

    public override Customer LoadCustomer(){
        // ..
    }
}

效果很好,除了 - 对于每个 OracleRepository - 我必须再次编写方法 EnsureIsConnected,我必须再次声明归档的 OracleConnection 等等。

我想抽象一下。我的第一个想法是使用Interface,但这并不能解决重复代码的问题。我的第二个想法是使用基类CustomerRepository 作为另一个新基类OracleRepository<T> 的泛型类型,然后定义通用例程。

问题是:据我所知,override 无法使用泛型类型的 virtual 方法,例如 CustomerRepository

这就是我认为它会起作用的方式。

public class OracleRepository<T> where T : class{

    public OracleRepository(){
        this.EnsureIsConnected());
    }

    protected OracleConnection oracleConnection;

    protected EnsureIsConnected(){
        // ...
    }
}

public class OracleCustomerRepository : OracleRepository<CustomerRepository>{

    // ..

    // this is not possible, defined in CustomerRepository
    public override Customer LostCustomer(){

    }
}

注意:CustomerRepository 不是我拥有的唯一存储库。这主要是我试图找到更好的解决方案的原因。

关于如何解决这个问题的任何建议?

【问题讨论】:

  • "据我所知,没有办法覆盖泛型类型的虚方法" - 你能提供一个你认为不可能的例子吗?
  • 可能写得不够清楚,但无论如何,如果你在基类中有虚方法,你可以覆盖它。通用虚拟方法可以像任何其他虚拟方法一样被覆盖
  • @MichaelKokorin 虚拟方法是在泛型类型中定义的,而不是在基类中。我会用一个例子来更新帖子。
  • “我想抽象 OracleConnection 的东西。”听起来您几乎可以在这里使用策略模式。您是否打算让CustomerRepository 为不同的 DB 类型使用不同的连接类型?
  • @MongZhu 每个不同的数据库类型都是CustomerRepository的新实现。

标签: c# .net generics inheritance interface


【解决方案1】:

你能做到吗?或者这不是您想要的?

public abstract class CustomerRepository<T>
{
    protected OracleConnection oracleConnection;

    protected CustomerRepository() : base(){
        if(EnsureIsConnected()){
            // ...
        }
    }

    protected bool EnsureIsConnected()
    {
        return true;
    }

    public abstract T Load();
}

public class OracleCustomerRepository : CustomerRepository<Customer>
{
    public override Customer Load(){
        return new Customer();
    }
}

【讨论】:

  • 我已经拥有的那种,我想抽象 OracleConnection 的东西。我已经更新了我的帖子。
  • 可以存在于抽象基类中,不是吗?
  • 这行得通,但这不是我拥有的唯一存储库。我真的不想为每个存储库创建这样一个基类,这只是将问题向下转移了一层。
  • 你能有一个抽象的泛型基类,然后可能带有 CRUD 操作吗?在具体的存储库中还有什么更具体的内容吗?
【解决方案2】:

我的第一个想法是您的抽象似乎有点泄漏。 我通常做的是创建与数据库引擎交互的接口,方法如下:

public interface IPersistence
{
    void ExecuteOperation(string prodecureName, Dictionary<string, object> parameters);
    T ExecuteSingle(string procedureName, Dictionary<string, object> parameters, Func<IDataReader, T> rowReader);
    List<T> ExecuteMultirow(string procedureName, Dictionary<string, object> parameters, 
Func<IDataReader, T> rowReader);
}

然后在存储库中,我将其作为依赖项,并从 IoC 容器中决定使用哪个实现。 所以在你的代码中你会有:

public class CustomerRepository
{
    private readonly IPersistence persistence;

    public CustomerRepository(IPersistence persistence)
    {
        this.persistence = persistence;
    }

    public virtual Customer LoadCustomer()
    {
        // preparation code
        return this.persistence.ExecuteSingle("some procedure", parameters, r => new Customer((int)r["Id"], (string)r["DisplayName"]));
    }
}

【讨论】:

  • 内部数据库例程已经定义,所以这不是问题。我想将常见的连接内容(例如检查连接是否可用)放在基类中,因为它总是相同的。但我也希望能够覆盖我在其他存储库类中定义的方法。
  • 我不确定检查连接可用性是否应该是存储库类的责任。我会在我执行查询的地方做。
  • 也许我写的有点不清楚:我需要在启动时验证与数据库本身的连接,每个执行查询的地方也会进行检查。但我需要在启动时进行初始检查以获取实例本身。
  • 但是如果您将此检查移至 IPersistence 实现的初始化,它将在 CustomerRepository 的初始化时进行检查。我不明白你为什么需要这个初始检查,但也许我只是不知道你正在处理的整个架构。
  • 看看我的 CustomerRepository 实现。存储库不实现 IPersistence。它只是获取它的实例并使用它的方法。你需要配置你的 IoC 来传递 OraclePersistence,它只会被实现一次。
猜你喜欢
  • 1970-01-01
  • 2012-03-01
  • 2019-08-12
  • 2012-04-25
  • 1970-01-01
  • 1970-01-01
  • 2013-08-20
  • 1970-01-01
  • 2016-11-17
相关资源
最近更新 更多