2014-11-05 188 views
0

我都此類工作作爲我的倉庫:具有非泛型方法約束的泛型類?

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

爲什麼'Repository'需要'new()'約束呢? – 2014-11-05 12:44:51

+0

@dav_i由於'GetByID'和其他類似的'Get'方法,我創建了一個T的新實例,填充它的數據並返回它。 – Danicco 2014-11-05 12:46:15

+0

無法達到此目的,但也可以使用類似AutoMapper的庫,並允許實施存儲庫,以確定如何將原始數據從存儲庫轉換爲要傳遞給自動映射器的數據傳輸對象。 – 2014-11-05 12:56:55

回答

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

不,你不能這樣來做。

所有的約束都必須指定引入泛型參數的地方,在這種情況下是在類級別。

因此,你有兩個選擇:

  1. 添加, new()作爲約束,限制使用存儲庫類的使用有一個公共的無參數的構造函數類型
  2. 沒有將其添加爲一個約束,而使用反射嘗試在運行時構造對象

請注意,如果類型沒有有效的構造函數,那麼點2可能會失敗(在運行時)。

你無法要求編譯器創建一個能夠調用特定方法的類,即有條件的,即。 「如果類型有構造函數,只讓我調用GetByID」。

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