C#:泛型类的具体重写

8

这是我正在使用的通用类:

 public interface IRepository<T> where T : EntityObject
{
    RepositoryInstructionResult Add(T item);
    RepositoryInstructionResult Update(T item);
    RepositoryInstructionResult Delete(T item);
}
public class Repository<T> : IRepository<T> where T : EntityObject
{

    RepositoryInstructionResult Add(T item)
    { //implementation}
    RepositoryInstructionResult Update(T item);
    { //implementation}
    RepositoryInstructionResult Delete(T item);
    { //implementation}
 }

现在我想偶尔改变方法的行为,当t : 一个特定的类型时。像下面这样的东西是可能的吗? 这个尝试会出现错误(错误5:'Repository'的部分声明必须按相同顺序具有相同的类型参数名称)。

public class Repository<Bar> //where Bar : EntityObject
{
    RepositoryInstructionResult Add(Bar item)
    { //different implementation to Repository<T>.Add() }
    //other methods inherit from Repository<T>
 }

2
BarRepository:Repository<Bar>...,并在基类/子类中将可重写的方法标记为virtual/override。 - Anthony Pegram
@Anthony:为什么不把那个作为答案发布呢?(我正准备发布它=P) - benjer3
4个回答

9
public class BarRepository : Repository<Bar>
{ 
    RepositoryInstructionResult Add(Bar item) 
    { //different implementation to Repository<T>.Add() } 
    //other methods inherit from Repository<T> 
} 

1
我对这个解决方案的反对意见是你需要显式地实例化 new BarRepository()。如果你调用 new Repository<Bar>(),你会得到通用(即错误的)实现。由于存储库已经在我的代码库中被创建和使用,我宁愿不必将实例化隐藏在工厂中。 - daveharnett
这段代码显然需要将方法公开为public,以满足接口的要求,并且需要在基类/子类中使用virtual/override修饰符,以便对行为进行多态处理。 - Anthony Pegram
@daveharnett,为了最好地使用存储库模式,依赖于存储库的代码或逻辑应编写为接口而非实现。然后,该代码将查看 IRepository<Bar>,因此不需要更改。尽管您是正确的,但负责创建具体实现的任何代码都需要更新。 - Anthony Pegram
1
@daveharnett,除了在您希望“覆盖”的方法中使用硬编码类型检查来污染Repository<T>之外,我不确定是否还有其他解决方法。仓储工厂是处理此情况的正确方法,其余代码只应关心IRepository<T>,使您能够随意按类型修改实现,从而提供灵活性。 - roken
@Anthony 除非有人提供了一个神奇的解决方案,否则这似乎是可行的方法。幸运的是,只有3/4个代码区域处理创建(一种懒惰实例化模式),所以如果我将它们指向一个工厂,改变不应该太痛苦。谢谢大家! - daveharnett

2

将你的存储库类命名为RepositoryBase,并使接口方法为虚方法。在RepositoryBase类中以一般方式实现它们,但因为你标记了这些方法为虚方法,所以你可以在派生类中重写功能。你的代码将类似于:

 public interface IRepository<T> where T : EntityObject
 {
    RepositoryInstructionResult Add(T item);
    RepositoryInstructionResult Update(T item);
    RepositoryInstructionResult Delete(T item);
 }

 public class Repository<T> : IRepository<T> where T : EntityObject
 {
    virtual RepositoryInstructionResult Add(T item)
    { //implementation}
    virtual RepositoryInstructionResult Update(T item);
    { //implementation}
    virtual RepositoryInstructionResult Delete(T item);
    { //implementation}
  }

如果您需要为 Bar 对象的更新方法执行一些自定义逻辑,只需创建派生类并命名为 BarRepository,并重写 Repositorybase 类的 update 方法。在此处,您可以调用基本实现或仅使用其自己的逻辑进行处理。

 public class BarRepository : Repositorybase<Bar>
 {
    public override RepositoryInstructionResult Update(Bar item);
    {
       //Call base method if needed
       //Base.Update(item);

       //implement your custom logic here
    }
  }

同我的回复roken一样。它可以完成工作,但会使消费更加困难。 - daveharnett
我的意思是你需要显式地实例化 new BarRepository()。如果你调用 new Repository<Bar>(),你会得到通用的(即错误的)实现。理想情况下,我正在寻找一种解决方案,可以隐藏在存储库创建代码中。 - daveharnett
你不必手动进行此操作,可以引入相同的命名约定,以便这些存储库可以自动创建。您需要使用Cativator.CreateInstance编写一次实例化逻辑。有很多方法可以实现您想要尝试的内容,但是这种交流可能会变成庞大的框架开发,所以我将决策留给您。 - Rati_Ge

0

使用扩展方法:

public static void DoSomething(this repository<Bar> repo)
{
  //your custom code goes here
}

0
作为对你问题的直接回答:最接近你所展示的东西的方法是在运行时检查T的实际值。在你的add方法中,你可以这样写:

if (typeof(T) == typeof(Bar)) {
    // your Bar-specific code
}
// ...

请注意,这种做法在性能方面可能不是很好,尤其是当你有一个以上的特殊类型需要单独处理时。
除此之外,唯一的解决方案是创建一个子类来指定基类的实际类型参数,具体细节可以参考其他回答中的说明。

谢谢。性能不考虑的话,如果有多个特殊情况,这将非常混乱。 - daveharnett
1
@daveharnett:不一定;在这种情况下,您可能希望使用Handler对象(自己的类)的Dictionary<Type,Handler>,执行特定于类型的操作。像这样,每个需要特殊处理的方法基本上只需要三到四行代码(可能使用扩展方法更少)。 - O. R. Mapper

网页内容由stack overflow 提供, 点击上面的
可以查看英文原文,
原文链接