簡體   English   中英

具有非泛型方法約束的泛型類?

[英]Generic class with non-generic method constraint?

我將此類用作我的存儲庫:

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

正如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沒有有效的構造函數,則第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>();

暫無
暫無

聲明:本站的技術帖子網頁,遵循CC BY-SA 4.0協議,如果您需要轉載,請注明本站網址或者原文地址。任何問題請咨詢:yoyou2525@163.com.

 
粵ICP備18138465號  © 2020-2024 STACKOOM.COM