2013-03-03 65 views
2

我已將自己編碼到一個角落,並希望您的幫助能夠再次挖掘我。在正確的方向。在C#中創建通用接口的實例的方法

所以,我已經實現了一個次要的SQLite的包裝,我想該解決方案是通用(我們不都是)。之後,我現在意識到這些類和接口的使用不是很直觀,也不是通用的。

讓我們從底部開始,向上工作。我創建了一個名爲DataRow的類,它用作我的表格行的基類。類DataRow本身只有一個屬性Id(因爲所有行都需要一個)。這導致瞭如下定義:class DataRow { public int Id { get; set; } }

使用這個DataRow類,是每個表。對於數據庫表,我創建了一個通用接口和一個通用基類。定義如下所示:

internal interface ITable<T> 
    where T : DataRow, new() 
{ 
    T Select(int id); 
    List<T> Select(List<int> ids); 
    int Insert(T t); 
    void Update(T t); 
    bool Delete(int id); 
} 

public class Table<T> : ITable<T> 
    where T : DataRow, new() 
{ 
    // Commented out to protect you from a minor case of serious brain damage. 
} 

此設置允許我創建簡明的定義。事實上,他們往往是相當史詩般的,真的。驕傲地說。

public class Car : DataRow 
{ 
    public decimal ParkingTicketDebt { get; set; } 
    public DateTime WhenWifeWillAllowReplacement { get; set; } 
    public bool CanTransformIntoSomethingAwesome { get; set; } 
} 

public class Cars : Table<Car> { 
    // Yep, that's all. You can go home now, folks. There's nothing here. Nothing at all. Especially not a great treasure of gold. Whops... I mean... there's really not. Not that I'm aware of, anyway... I mean, there could be. Not that I wouldn't say if I had any information on this great trasure of gold that might exist. But I know nothing of such an item. I really don't, so you can stop thinking about this great treasure of gold. Since I don't know anything about it, the chance that it even exist is extremely low. Miniscule. I mean, you would probably not find anything, not even if you digged for, like, a really long time. Seven years or something. Oookay. Slowly fading away... 
} 

正如你可能會或可能沒有注意到,我使用的Cars類的類型名稱,以確定在數據庫中的表的名稱。同樣,我正在對Car進行反思,並使用其公共屬性名稱和類型來獲取/設置數據庫中的值。是的,我知道我正在編寫一個精簡版的實體框架。這聽起來既愚蠢又相當耗時。

不管怎麼說,這是類Cars,我必須提醒你的使用的例子,我的驕傲:

new Cars().Delete(3497); // Note that I have a great number of (expensive) cars. 

不錯,不是嗎?一個小問題。這意味着我必須編寫強類型代碼,特定於數據庫中存在的表的數量。我不喜歡特定的代碼。我喜歡通用代碼。

你可能會開始在這裏爭論我正在過度使用它。那麼讓我告訴你這一點。你真是太棒了我太過分了!我故意火焰噴射被坦克擊倒的死人。七次。

於是我開始嘗試了一下,用這種微妙的解決方案提出了:

[ScriptMethod(ResponseFormat = ResponseFormat.Json)] 
[WebMethod(EnableSession = true)] 
public int CreateEmptyRow(string tableName) 
{ 
    var tableType = Type.GetType(tableName); 
    if (tableType == null) 
     throw new TypeLoadException("Dumbass. That table doesn't exist"); 

    var instance = Activator.CreateInstance(tableType) as ITable<dynamic>; 
    if (instance == null) 
     throw new TypeLoadException("Idiot. That type isn't a table"); 

    return instance.Insert(new DataRow()); 
} 

請注意,如果你不知道我能真正理解爲什麼會有人想要創建一個空行。

那麼這有什麼問題呢?那麼,它不會編譯,一個。這是錯誤:There is no implicit reference conversion from 'dynamic' to 'DataRow'。在Google上搜索了few results

問題明顯是Activator.CreateInstance(tableType) as ITable<dynamic>。我試過了Activator.CreateInstance(tableType) as ITable<Table<DataRow>>之類的東西,這個錯誤給了我一個錯誤:The type 'DataRow' must be convertible to 'DataRow'

+0

關於改進我的解決方案的建議將獎勵...獎勵 – Simeon 2013-03-03 13:21:50

+0

由於您已經知道您的Cars類實現了'ITable ',您是否需要將其轉換爲接口?你能否簡單地放棄'作爲ITable '?我發現這篇文章在過去非常有幫助:[如何使用反射檢查和實例化泛型](http://msdn.microsoft.com/zh-cn/library/b8ytshk6.aspx) – plasticide 2013-03-03 13:24:27

+0

解決方案的變化最少,也許不是最快/最有效的,將反映你想要調用的方法(而不僅僅是你實例化的類型) – YavgenyP 2013-03-03 13:29:27

回答

1

所以,香港專業教育學院寫在評論,進出口增加額外的非通用接口:

interface ITable 
{ 
    DataRow Select(int id); 
    IEnumerable<DataRow> Select(List<int> ids); 
    int Insert(DataRow t); 
    void Update(DataRow t); 

} 
interface ITable<T> where T : DataRow, new() 
{ 
    T Select(int id); 
    List<T> Select(List<int> ids); 
    int Insert(T t); 
    void Update(T t); 
    bool Delete(int id); 
} 

class Table<T> : ITable<T>, ITable where T : DataRow, new() 
{ 

    public T Select(int id) 
    { 
     return new T(); 
    } 

    public List<T> Select(List<int> ids) 
    { 
     return new List<T>(); 
    } 

    public int Insert(T t) 
    { 
     return 1; 
    } 

    public void Update(T t) 
    { 
    } 

    public bool Delete(int id) 
    { 
     return true; 
    } 

    DataRow ITable.Select(int id) 
    { 
     return this.Select(id); 
    } 

    IEnumerable<DataRow> ITable.Select(List<int> ids) 
    { 
     return this.Select(ids); 
    } 

    public int Insert(DataRow t) 
    { 
     return this.Insert(t); 
    } 

    public void Update(DataRow t) 
    { 
     this.Update(t); 
    } 
} 

,這是如何實現的CreateEmptyRow \ Select方法IM:

 public static int CreateEmptyRow(string tableName) 
    { 
     var tableType = Type.GetType(tableName); 
     if (tableType == null) 
      throw new TypeLoadException("Dumbass. That table doesn't exist"); 

     var instance = Activator.CreateInstance(tableType) as ITable; 
     if (instance == null) 
      throw new TypeLoadException("Idiot. That type isn't a table"); 

     return instance.Insert(new DataRow()); 
    } 
    public static List<DataRow> Select(List<int> ids, string tableName) 
    { 
     var tableType = Type.GetType(tableName); 
     if (tableType == null) 
      throw new TypeLoadException("Dumbass. That table doesn't exist"); 

     var instance = Activator.CreateInstance(tableType) as ITable; 
     if (instance == null) 
      throw new TypeLoadException("Idiot. That type isn't a table"); 

     return instance.Select(ids).ToList(); 
    } 

通知,如果你想這樣一個通用的解決方案,選擇方法(例如)只能返回IEnumerable \的DataRowList,這

var myList = Select(null, "Cars").Cast<Car>(); 

注:可以使用所提供的Cast擴展方法來解決你可能知道,實例化Cars類的名字,你還需要提供namespac e,我在這裏跳過了,可能Table<T>類也應該是抽象的。

+0

這不起作用,我必須創建一個額外的接口'IDataRow',因爲'DataRow'不能轉換爲'T'。但是我的最終解決方案最終與你的結果非常接近。非常感謝!我會發布我的最終解決方案,爲他人的個人樂趣 – Simeon 2013-03-04 19:13:36

+0

酷,很高興我可以幫助:) – YavgenyP 2013-03-04 19:32:45

1

一個問題是你試圖插入一個DataRow到一個需要DataRow的子類的表中,所以即使你可以編譯它,你仍然會在運行時得到一個異常。

你需要找到通用列式插入,插入該類型的新實例:

object instance = Activator.CreateInstance(tableType); 
var tableInterface = tableType.GetInterfaces().FirstOrDefault(it => it.IsGenericType && it.GetGenericTypeDefinition() == typeof(ITable<>)); 
if(tableInterface == null) throw new ArgumentException("Type is not a table type"); 

var rowType = tableInterface.GetGenericArguments()[0]; 
var newRow = Activator.CreateInstance(rowType); 

MethodInfo insertMethod = tableInterface.GetMethod("Insert"); 
return (int)insertMethod.Invoke(instance, new object[] { newRow }); 

但是似乎你可以使你的CreateEmptyRow方法在錶行類型通用的,完全避免反射:

public int CreateEmptyRow<TTable, TRow>() 
    where TRow : DataRow, new() 
    where TTable : ITable<TRow>, new() 
{ 
    var table = new TTable(); 
    return table.Insert(new TRow()); 
} 
+0

對不起,我很困惑。這是一種Web服務方法。我會編輯我的問題! – Simeon 2013-03-03 13:42:56

+0

這給了我一個'RuntimeBinderException'說'最好的重載方法匹配'表.Insert(Car)'有一些無效的參數'。在檢查'newRow'時,我可以看到它是'Car'的完美實例。有任何想法嗎? – Simeon 2013-03-03 13:53:24

+0

@Simeon - 我改變了代碼來通過反射而不是使用動態調用'Insert'方法 - 是否解決了這個問題? – Lee 2013-03-03 14:01:53