2013-03-03 66 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