c# - 同时继承基类和接口(interface)

标签 c# interface base-class

在本类(class)项目中,教师创建了一个用于数据访问的抽象基类 (EfEntityRepositoryBase),这是一个继承抽象基类的每个实体的具体类 (ProductDal)并实现一个接口(interface) (IEntityRepository)。 ProductDal 还有其接口(interface) (IProductDal),它也实现了 IEntityRepository。

这样做的用例是什么? 我无法理解 IProductDal 实现 IEntityRepository 的意义,因为 ProductDal 已经继承了实现相同接口(interface)的抽象基类。因此,如果 IEntityRepository 中有任何函数更新,应该没有问题。如果有人可以解释那就太好了。下面是抽象类和接口(interface)代码。

public class ProductDal : EfEntityRepositoryBase<Product>, IProductDal{ }

public interface IEntityRepository<T>
{
    void Add(T entity);
    void Delete(T entity);
    void Update(T entity);
    List<T> GetAll(Expression<Func<T, bool>> expression = null);
    T GetById(Expression<Func<T, bool>> expression);
}

public interface IProductDal: IEntityRepository<Product>
{
}

public class EfEntityRepositoryBase<TEntity> : IEntityRepository<TEntity> where TEntity : class, IEntity, new()
{
    public void Add(TEntity entity)
    {
        using (BookStoreTrackerDBContext context = new BookStoreTrackerDBContext())
        {
            var addedEntity = context.Entry(entity);
            addedEntity.State = EntityState.Added;
            context.SaveChanges();
        }
    }
}

最佳答案

我认为很容易理解,当您查看您提供的示例时,很想喊出 IProductDal 。接口(interface)是多余的。事实上,它没有向类型 ProductDal 添加任何额外的成员。因为接口(interface)IProductDal和通用类 EfEntityRepositoryBase使用相同的泛型参数类型 Product 进行定义。由于这些教学示例不是在真实的应用程序代码背景下设置的,因此它们背后的真实意图或想法并不容易理解。


作为旁注,您应该知道,如果类 EfEntityRepositoryBase<TEntity>将使用与 Product 不同的泛型参数类型来定义例如,int , ProductDal将有 IEntityRepository<T> 的两个实现/成员重载界面。 例如:

public class ProductDal : EfEntityRepositoryBase<int>, IProductDal
{ 
  // Implementation of IProductDal. The EfEntityRepositoryBase type provides another 'int' overload
  public void Add(Product entity) {}
}

void Main()
{
  var productDal = new ProductDal();
  
  // Implementation of IEntityRepository<int> provided by class EfEntityRepositoryBase<int>
  productDal.Add(6);

  // Implementation of 'IProductDal' (provided by class 'ProductDal')  
  productDal.Add(new Product());
}

您可以看到您提供的示例显示了一种特殊情况,其中 EfEntityRepositoryBase<TEntity>已经提供了 IEntityRepository<Product> 的实现和 IProductDal 接口(interface)。


回到你的例子:如果你使用类型转换,你会发现另一个使用所谓冗余类型定义的用例:

给定的是你的ProductDal键入以下类签名

public class ProductDal : EfEntityRepositoryBase<int>, IProductDal

您现在有多种类型可用于访问 IEntityRepository<Product> 的实现

void Main()
{
  // Create an instance of ProducDal
  ProductDal productDal = new ProductDal();

  /* Use the instance of ProductDal with multiple overloads 
     to show implicit type casting */
  UseProductDal(productDal);
  UseIProductDal(productDal);
  UseIEntityRepository(productDal);
  UseEntityRepository(productDal);
}

void UseProductDal(ProductDal productDal)
{
  // Instantiate the argument
  var product = new Product(); 
  productDal.Add(product);
}

void UseIProductDal(IProductDal productDal)
{
  // Instantiate the argument
  var product = new Product(); 
  productDal.Add(product);
}

void UseIEntityRepository(IEntityRepository<Product> productDal)
{
  // Instantiate the argument
  var product = new Product(); 

  productDal.Add(product);
}

void UseEntityRepositoryBase(EntityRepositoryBase<Product> productDal)
{
  // Instantiate the argument
  var product = new Product(); 
  productDal.Add(product);
}

这展示了如何使用隐式类型转换以及如何使用接口(interface)。
您现在看到虽然 EntityRepositoryBase<Product>已经实现 IEntityRepository<Product> ,仍然有ProductDal另外实现IProductDal接口(interface)对于启用 ProductDal 非常有意义仅在 IProductDal 的情况下使用接口(interface)是已知的。


您可以利用界面转换来隐藏成员。例如,如果您向每个接口(interface)添加独占成员,则只有在将实现者强制转换为相应的接口(interface)时才能访问该成员:

public interface IEntityRepository<T>
{
  void Add(T entity);  
}

public interface IProductDal: IEntityRepository<Product>
{
  // Exclusive member. Will be only visible when accessed through this interface. 
  int GetProductCount();
}

给定的是你的ProductDal键入以下类签名

public class ProductDal : IEfEntityRepository<int>, IProductDal

void Main()
{
  // Create an instance of ProducDal
  ProductDal productDal = new ProductDal();

  /* Use the instance of ProductDal with multiple overloads 
     to show implicit type casting */
  UseProductDal(productDal);
  UseIProductDal(productDal);
  UseIEntityRepository(productDal);
  UseEntityRepository(productDal);
}

// All implemented interfaces are visible since there is no casting involved.
// All members are referenced via the implementor type ProductDal.
void UseProductDal(ProductDal productDal)
{
  // Instantiate the argument
  var product = new Product(); 
  
  productDal.Add(product);
  int productCount = productDal.getProductCount();
}

// Only 'IProductDal' is visible since there is an implicit cast to an interface type involved
void UseIProductDal(IProductDal productDal)
{
  // Instantiate the argument
  var product = new Product(); 

  // 'Add()' is provided by 'IEntityRepository<T>', 
  // which is implemented by 'IProductDal' and therefore "visible"
  productDal.Add(product); 

  // 'GetProductCount()' is provided by 'IProductDal'
  int productCount = productDal.GetProductCount();
}

// Only 'IEntityRepository<T>' is visible since there is an implicit cast to the interface type 
void UseIEntityRepository(IEntityRepository<Product> productDal)
{
  // Instantiate the argument
  var product = new Product(); 

  productDal.Add(product);

  // 'GetProductCount()' is only available via the 'IProductDal' interface. 
  // It's not visible here.
  //int productCount = productDal.GetProductCount();
}

关于c# - 同时继承基类和接口(interface),我们在Stack Overflow上找到一个类似的问题: https://stackoverflow.com/questions/69336083/

相关文章:

c++ - 隐藏不应该在只允许 const 访问的基类中改变的成员变量,这样就可以保留赋值运算符

c# - 无法删除目录 ACE

c# - 将外部 C/C++ CUDA 库与 C# 结合使用

c# - C# 中的数字声音处理(可能还有 SilverLight)

java - 一个类的例子如何改变它的行为? Java接口(interface)使用

接口(interface)的 TypeScript 反射

java - 在 Activity1 fragment 上使用从 Activity2 接口(interface)检索的数据时出现问题

c# - 通过基类c#实现的接口(interface)调用类的方法

c# - 当构造函数使用 1 个参数但 base 关键字使用 2 个参数时会发生什么

c# - 将url参数字符串转换为路径的正则表达式