c#中创建泛型接口实例的方法
本文关键字:方法 实例 泛型接口 创建 | 更新日期: 2023-09-27 18:01:51
我已经把自己写进了一个角落,希望你能帮我再把我挖出来。在正确的方向。
所以,我实现了一个小的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'
。在谷歌上搜索几乎没有结果。
问题显然是Activator.CreateInstance(tableType) as ITable<dynamic>
。我已经尝试过Activator.CreateInstance(tableType) as ITable<Table<DataRow>>
,尝试给了我这个错误:The type 'DataRow' must be convertible to 'DataRow'
.
所以,正如我在评论中所写的,我添加了一个额外的非泛型接口:
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
方法:
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();
}
注意,如果您想要这样一个通用的解决方案,select方法(例如)只能返回DataRow
的IEnumerable
' List
,这可以通过使用提供的Cast
扩展方法来解决:
var myList = Select(null, "Cars").Cast<Car>();
注意:您可能知道,要按名称实例化Cars
类,还需要提供名称空间,我在这里跳过了,并且Table<T>
类可能也应该是抽象的。
一个问题是你试图将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());
}