具有非泛型方法约束的泛型类

本文关键字:泛型类 约束 泛型方法 | 更新日期: 2023-09-27 18:29:24

我有这个类作为我的存储库:

public class Repository<T> where T : class, new()
{
    public T GetByID(int id)
    {
        //Code...
    }        
}

但在一些情况下,我不想留下类的默认公共构造函数(例如一些需要一些逻辑的特定模型属性),比如:

public class Person
{
    public CPersonID PersonID { get; private set; }
    //This shouldn't exist outside Person, and only Person knows the rules how to handle this
    public class CPersonID 
    {
        internal CPersonID() { }
    }
}

这使得Repository模板类由于new()约束而无效。我想做这样的东西:

public class Repository<T> where T : class
{
    //This function should be created only when the T has new()
    public GetByID(int id) where T : new()
    {            
    }
    //And this could be the alternative if it doesn't have new()
    public GetByID(T element, int id)
    {
    }
}

我有什么办法可以做到这一点吗?

编辑:Get方法示例:

public IList<T> GetAll()
{
    IList<T> list = new List<T>();
    using(IConnection cn = ConnectionFactory.GetConnection())
    {
        ICommand cm = cn.GetCommand();
        cm.CommandText = "Query";
        using (IDataReader dr = cm.ExecuteReader())
        {
            while(dr.Read())
            {
                T obj = new T(); //because of this line the class won't compile if I don't have the new() constraint
                //a mapping function I made to fill it's properties
                LoadObj(obj, dr);
                list.Add(obj);
            }
        }
    }
    return list;
}

具有非泛型方法约束的泛型类

正如Lasse V.Karlsen已经回答的那样,这是不可能的。然而,你可以离得很近,足够近以达到实际目的。

给定public class Repository<T> where T : class,您不能定义只有当T具有无参数构造函数时才存在的实例方法。你不需要那个。你只需要repository.GetByID(3)就可以工作了。如果GetByID是一个实例方法,那么它也可以工作,但如果它是一个扩展方法,并且扩展方法可以向T添加需求。

public static class RepositoryExtensions
{
  public T GetByID(this Repository<T> repo, int id) where T : class, new()
  {
    ...
  }
}

请注意,如果已经存在同名的实例方法,则扩展方法不起作用,因此,如果使用此方法,则需要GetByID的两个重载都是扩展方法,而不仅仅是此重载。

实际的逻辑属于Repository类,但您可以转发到:

public class Repository<T> where T : class
{
  internal T GetByIDImpl(int id, Func<T> factory)
  {
    ...
  }
}
public static class RepositoryExtensions
{
  public T GetByID(this Repository<T> repo, int id) where T : class, new()
  {
    return repo.GetByIDImpl(id, () => new T());
  }
  public T GetByID(this Repository<T> repo, T element, int id) where T : class
  {
    return repo.GetByIDImpl(id, () => element);
  }
}

不,你不能这样做。

所有约束都必须在引入泛型参数的地方指定,在本例中是在类级别。

因此,您有两种选择:

  1. 添加, new()作为约束,将存储库类的使用限制为使用具有公共无参数构造函数的类型
  2. 不将其添加为约束,而是使用反射尝试在运行时构造对象

请注意,如果类型没有有效的构造函数,则第2点可能会失败(在运行时)。

没有办法要求编译器创建一个类,在该类中,调用特定方法的能力是有条件的,即"只有当类型有构造函数时,才让我调用GetByID"。

如果您想将其作为编译时间约束,可以执行

public class Class<T> where T : class
{
    public void Method<U> where U : T, new()
    {
        // ...
    }
}

但这有一个缺点,那就是你必须进行

new Class<HasConstructor>().Method<HasConstructor>();

因为该类型不会被隐式地拾取。优点是以下内容不会编译:

new Class<NoConstructor>().Method<NoConstructor>();