简体   繁体   English

C#OOP:在基础混凝土类中使用带有类型参数的接口

[英]C# OOP : Using interface with type parameters in base concrete class

I hope the title is descriptive of the problem, I had trouble phrasing the problem I am trying to solve. 我希望标题能说明问题,但我在尝试解决问题时会遇到麻烦。

I'm using .NET Core 2.1 我正在使用.NET Core 2.1

I've recently started at a C# shop after doing Python for the last 5 years so my strongly typed oop is a bit rusty. 最近五年来,我在使用Python后开始在C#商店工作,所以我的强类型oop有点生锈。 Here is what I have and what I'm trying to do: 这是我所拥有的以及我正在尝试做的事情:

I a base repository interface that defines basic CRUD functionality for database entities: 我是一个基本的存储库接口,它为数据库实体定义了基本的CRUD功能:

public interface IRepository<TEntity, TPrimaryKey> where TEntity : class
{
   TEntity FindById(TPrimaryKey id)
   void Create(TEntity entity);
   void Delete (TEntity entity);
}

I inherit from this interface to define the interface that my repository class implements: 我从该接口继承来定义我的存储库类实现的接口:

interface IProductRepository : IRepository<Product, int>
{
   void SomeProductRepoMethod(int someParam);
}

Then I implement all interface methods in my concrete class: 然后,在具体的类中实现所有接口方法:

public class ProductRepository : IProductRepository
{
   public Product FindById(int id)
   {
      // lookup
   }

   public void Create(Product product)
   {
      // save 
   }

   public void SomeProductRepoMethod(int someParam)
   {
      // do product repository specific stuff
   }
   ....
}

Now, what I want to do is fairly straightforward. 现在,我想做的非常简单。 I want to add a overload of Create on IRepository that takes an IEnumerable of TEntity : 我想在IRepository上添加Create的重载,该重载使用TEntityIEnumerable

public interface IRepository<TEntity, TPrimaryKey> where TEntity : class
   ...
   void Create(IEnumerable<TEntity> entities);
   ...

But I'd like to define the implementation of this overload once : So I was thinking I could make a abstract base repository class to put the above implementation. 但是我想一次定义此重载的实现 :所以我想我可以制作一个抽象的基础存储库类来放置上述实现。 The problem I am facing is I'm not sure how or even if I could do this cleanly with the modelI have now. 我面临的问题是我不确定如何或什至可以用我现在拥有的模型干净地做到这一点。 I tried to make a base class that implements IRepository , but that would mean passing type params to the base class and on to the interface: 我试图制作一个实现IRepository的基类,但这意味着将类型参数传递给基类并传递给接口:

public abstract class BaseRepository<TEntity, TPrimaryKey> : IRepository<TEntity, TPrimaryKey>
{
    public abstract TEntity FindById(TPrimaryKey id);

    public abstract void Create(TEntity entity);
    public abstract void Delete(TEntity entity);
    public void Create(IEnumerable<TEntity> entities)
    {
        foreach(TEntity entity in entities)
        {
            Create(entity);
        }
    }
}

Then in my concrete repository: 然后在我的具体存储库中:

public class ProductRepository : BaseRepository<Product, int>, IProductRepository
{
   public override Product FindById(int id)
   {
      // lookup
   }

   public override void Create(Product product)
   {
      // save 
   }

   public void SomeProductRepoMethod(int someParam)
   {
      // do product repository specific stuff
   }
   ....
}

This doesn't feel quiet right to me since I am passing the same type params in both IProductRepository and ProductRepository . 因为我在IProductRepositoryProductRepository传递相同类型的参数,所以这对我来说并不安静。 I feel like I'm close but not there and I'm not sure what the best practice approach here would be. 我感觉自己已经接近但不在那儿,而且我不确定这里的最佳实践方法是什么。 If anyone could suggest an approach I would really appreciate the feedback. 如果有人可以建议一种方法,我将非常感谢您的反馈。 Apologies for the length of the post but I felt I needed to clearly describe what I was trying to do. 很抱歉,这篇文章很长,但我觉得我需要清楚地描述自己想做的事情。 Thanks! 谢谢!

Having the same type parameters in an interface and an abstract class is not that big of a deal. 在接口和抽象类中具有相同的类型参数没什么大不了的。 Using your abstract class solution is okay, unless your ProductRepository needs to inherit from some other class. 除非您的ProductRepository需要从某个其他类继承,否则可以使用抽象类解决方案。

Actually, with your abstract class, your IRepository interface doesn't need to exist anymore. 实际上,对于您的抽象类,您的IRepository接口不再需要存在。 Just handle everything with BaseRepository ! 只需使用BaseRepository处理一切!

Another solution to this problem is an extension method. 解决此问题的另一种方法是扩展方法。 In a static class, you can write this: 在静态类中,您可以这样编写:

public static void Create<TEntity, TPrimaryKey>(this IRepository<TEntity, TPrimaryKey> repo, IEnumerable<TEntity> entities) where TEntity : class {
    // do your foreach loop here
} 

Now you can call this method on any IRepository instance just like this: 现在,您可以在任何IRepository实例上调用此方法,如下所示:

repository.Create(...);

Here's the way I'd do it. 这就是我要做的方式。 I'd break the inheritance between IRepository and IProductRepository : 我会打破IRepositoryIProductRepository之间的继承关系:

Here are your interfaces: 这是您的界面:

public interface IRepository<TEntity, TPrimaryKey> where TEntity : class
{
    TEntity FindById(TPrimaryKey id);
    void Create(TEntity entity);
    void Delete(TEntity entity);
    void Create(IEnumerable<TEntity> entities);
}

internal interface IProductRepository
{
    void SomeProductRepoMethod(int someParam);
}

Then let your base class inherit IRepository as you've done: 然后,让您的基类像您一样继承IRepository

base class: 基类:

public abstract class BaseRepository<TEntity, TPrimaryKey> : 
    IRepository<TEntity, TPrimaryKey> where TEntity : class
{
    public abstract TEntity FindById(TPrimaryKey id);
    public abstract void Create(TEntity entity);
    public abstract void Delete(TEntity entity);

    public void Create(IEnumerable<TEntity> entities)
    {
        foreach (TEntity entity in entities)
        {
            Create(entity);
        }
    }
}

and then you derive your base class and also implement your IProductRepository : 然后派生基类并实现IProductRepository

public class ProductRepository : BaseRepository<Product, int>, IProductRepository
{
    public override Product FindById(int id)
    {
        // find
    }

    public override void Create(Product product)
    {
        // save 
    }

    public void SomeProductRepoMethod(int someParam)
    {
        // do product repository specific stuff
    }

    public override void Delete(Product entity)
    {
        // delete
    }
}

I think your derived class' specificity to being a Product repository is an implementation detail on BaseRepository . 我认为您的派生类对Product存储库的特殊性是BaseRepository上的实现细节。

声明:本站的技术帖子网页,遵循CC BY-SA 4.0协议,如果您需要转载,请注明本站网址或者原文地址。任何问题请咨询:yoyou2525@163.com.

 
粤ICP备18138465号  © 2020-2024 STACKOOM.COM