如何编写一个既可继承又可泛型的类?

Ahm*_*mad 1 c# generics inheritance

我的项目中有几个类映射到数据库中的表行。大多数类共享大量样板代码以从数据库中获取数据FetchAll,并根据给定的 id 返回某些对象/行FetchById()

我想编写一个可以执行单一类FetchAllFetchById(),然后我的所有其他类都可以从该对象继承。但是,我正在努力将基类定义为泛型。其他类不能继承它,同时属于与它们继承的内容相关联的泛型类型。

为了更好地解释我的问题,我将开始简化共享相似代码的两个常见类:

人物.cs

class Person {
   public int Id { get; set; }
   public string Name { get; set; }
   
   public static List<Person> All {
      get { /* fetch all rows and */ return List<Person>; }
   }
   public static Person FetchById(int id) {
      return All.Where(p => p.Id == id).SingleOrDefault();
   }
}
Run Code Online (Sandbox Code Playgroud)

部门.cs

class Department {
   public int Id { get; set; }
   public string Name { get; set; }
   public string CostCenter { get; set; }

   public static List<Department> All { get { /* same logic as with Person */ } }
   public static Department FetchById(int id) { /* same logic as with Person */ }
}
Run Code Online (Sandbox Code Playgroud)

现在这是我尝试做的以减少代码重复:

IIidentifiable.cs

public interface IIdentifiable {
   // make sure all objects have an Id property
   int Id { get; set; }
}
Run Code Online (Sandbox Code Playgroud)

数据库对象

class DbObject<T> where T: IIdentifiable {
    public static List<T> All { get {  /* return List<T>; */ } }
    public static T FetchById(int id) {
       return All.Where(object => object.Id == id).FirstOrDefault();
    }
}
Run Code Online (Sandbox Code Playgroud)

现在我回到我PersonDepartment班级来利用DbObject

Person.cs(修改)

class Person : DbObject<Person>, IIdentifiable // won't compile: The type cannot be used as type parameter T
{
    public int Id { get; set; }    
    public string Name { get; set; }

   // public static List<Person> All {get{ ... }};    // should be inherited
   // public static Person FetchById(int id) { ... }  // should be inherited
}
Run Code Online (Sandbox Code Playgroud)

我对Department. 你明白了。我觉得我做错了什么。我知道它应该比这简单得多。如何同时使DbObject泛型和可继承?

Eni*_*ity 5

您可以通过采用Curiously recurring template pattern来做到这一点。

有效地定义DbObject<T>这样T : DbObject<T>它就可以工作了。

public class DbObject<T> where T : DbObject<T>, IIdentifiable
{
    public List<T> All { get { return new List<T>(); } }
    public T FetchById(int id)
    {
        return All.Where(x => x.Id == id).FirstOrDefault();
    }
}

public class Person : DbObject<Person>, IIdentifiable
{
    public int Id { get; set; }
    public string Name { get; set; }
}
Run Code Online (Sandbox Code Playgroud)

现在这段代码有效:

Person p = new Person();

List<Person> all = p.All;
Run Code Online (Sandbox Code Playgroud)

在您的原始代码中,您使用的是static方法。我认为是这样你可以写List<Person> all = Person.All;,但你不能继承static方法。

您可能需要考虑创建一个负责返回人员的存储库对象。


请记住,这种方法的缺点是您需要小心。这段代码可以编译,但可能不是你想要的:

public class Department : DbObject<Person>, IIdentifiable
{
    public int Id { get; set; }
    public string Name { get; set; }
}
Run Code Online (Sandbox Code Playgroud)