【问题标题】:Generic class with non-generic method constraint?具有非泛型方法约束的泛型类?
【发布时间】:2014-11-05 21:11:22
【问题描述】:

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

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

【问题讨论】:

  • 为什么Repository 需要有new() 约束?
  • @dav_i 因为GetByID 和其他类似的Get 方法,我创建了一个新的T 实例,填充它的数据,然后返回它。
  • 您无法以您想要的方式实现这一点,但是您也可以使用 AutoMapper 之类的库,并允许存储库的实现确定如何将存储库中的原始数据转换为数据传输对象传递给自动映射器。

标签: c# generics type-constraints


【解决方案1】:

正如 Lasse V. Karlsen 已经回答的那样,这是不可能的。但是,您可以非常接近,足够接近实用目的。

给定public class Repository&lt;T&gt; 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】:

    不,你不能这样做。

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

    因此,您有两个选择:

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

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

    您无法要求编译器创建一个调用特定方法的能力是有条件的类,即。 “只有在类型有构造函数时才让我调用 GetByID”。

    【讨论】:

      【解决方案3】:

      如果你希望它作为编译时约束,你可以这样做

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

      【讨论】:

        猜你喜欢
        • 1970-01-01
        • 2015-05-02
        • 1970-01-01
        • 1970-01-01
        • 1970-01-01
        • 1970-01-01
        • 1970-01-01
        • 2012-06-05
        相关资源
        最近更新 更多