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

时间:2014-11-05 12:41:10

标签: c# generics type-constraints

我让这个班作为我的存储库:

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() { }
    }
}

由于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;
}

3 个答案:

答案 0 :(得分:2)

不,你不能这样做。

必须在引入泛型参数的位置指定所有约束,在本例中为类级别。

因此,您有两种选择:

  1. 添加, new()作为约束,限制使用存储库类来使用具有公共无参数构造函数的类型
  2. 不将其添加为约束,并使用反射尝试在运行时构造对象
  3. 注意,如果类型没有有效的构造函数,那么第2点可能会失败(在运行时)。

    你无法让编译器创建一个类,其中调用特定方法的能力是有条件的,即。 “如果类型有构造函数,我只能调用GetByID。”

答案 1 :(得分:2)

正如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);
  }
}

答案 2 :(得分:0)

如果您希望将其作为编译时约束,则可以执行

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>();