我今天早上一直在寻找一种从System.Data.DataTable创建特定对象列表的方法。到目前为止,我在我的DomainModel基类中有这个:
protected static List<T> ConvertTo<T>(DataTable dt) where T : class, new()
{
int count = dt != null ? dt.Rows.Count : 0;
List<T> list = new List<T>(count);
if (dt != null & dt.Rows.Count > 0)
{
foreach (DataRow row in dt.Rows)
{
T item = new T(); // ????
FillObjectFromDataRow(item, row);
list.Add((T)item);
}
}
return list;
}
注意:我想要返回一个空列表,因为它主要只是绑定到数据网格,转发器等。
然而,它不起作用,因为类通常有一个私有构造函数来防止未经授权的实例化(我的意思是这样),所以我得到“类型'typename'必须是一个非抽象类型与公共无参数构造函数“错误。
我不愿意向我的类引入一个公共无参数构造函数,因为它们包含90%的静态方法(与ObjectDataSources一起使用)并且创建一个“空”类是没有意义的。即新的Employee对象将为空,而不是由公共静态Employee Get(字符串employeeID)创建,它将包含更多有用的信息。
关于如何在'????'创建T的“东西”的任何想法标记行而不使用new()约束?我可以通过Type parm实例化“事物”吗?
感谢您的任何想法,只是想通过这个来尝试在我的应用中干......
亲切的问候,
Mike K。
PS是的,也许是dt.Rows.Count&gt; 0呼叫是不必要的......
答案 0 :(得分:2)
您可以使用Activator.CreateInstance<T>()
或通过typeof(T).GetConstructor(...).Invoke(new object[] { ... });
如果你需要很多这样的操作;您也可以使用Expression.New
创建它们;作为CreateInstance
并通过构造函数是相当昂贵的方法。
以基于表达式的方式进行的示例。
private void Foo()
{
TestJan myObject = CreateObject<TestJan>("Jan", 21);
}
private T CreateObject<T>(string name, int age)
{
//first get a reference to the ConstructorInfo
//we know constructor has 2 params, string and int32. Names are not important.
System.Reflection.ConstructorInfo ci = typeof(T).GetConstructor(new[] { typeof(string), typeof(int) });
//we now have to define the types
ParameterExpression stringParam = Expression.Parameter(typeof(string), "stringExp");
ParameterExpression intParam = Expression.Parameter(typeof(int), "intExp");
//create an expression
NewExpression constructor = Expression.New(ci, stringParam, intParam);
//wrap this in a lambda-expression, which returns basically
// (stringExp, intExp) => new T(stringExp, intExp);
LambdaExpression lambda = Expression.Lambda(constructor, stringParam, intParam);
//compile into delegate
var constructorDelegate = (Func<string, int, T>)lambda.Compile();
//invoke the delegate. Normally you would cache this in a static Dictionary<Type, Delegate>.
//when you cache this, it's lightning fast. As it's just as fast as hard program
// (stringExp, intExp) => new T(stringExp, intExp);
return constructorDelegate.Invoke(name, age);
}
您可以根据参数改变当然,我发现这是创建对象的最佳方式,因为它既快速又灵活。
答案 1 :(得分:2)
正如已经提到的那样,Activator.CreateInstance
方法应该完成这项工作 - class, new()
是便利,并表达我们知道 应该的内容当然,情况......你有什么特别的理由不想要它吗?
另一种方法是传递工厂方法 - 这可能特别有助于任何DI问题等:
protected static List<T> ConvertTo<T>(DataTable dt, Func<T> ctor)
{
{loop}
T newItem = ctor();
{fill row}
{/loop}
}
然后你可以打电话给(例如):
List<Person> people = ConvertTo<Person>(dataTable, () => new Person());
或:
List<Person> people = ConvertTo<Person>(
dataTable, () => diContainer.Create<Person>()); // for your DI service
答案 2 :(得分:1)
您可以使用Activator.CreateInstance<T>()
。
编译器使用CreateInstance泛型方法来实现类型参数指定的类型的实例化。
答案 3 :(得分:1)
反射解决方案的替代方案可能是使用创建者lambda:
protected static List<T> ConvertTo<T>(DataTable dt, Func<DataRow, T> create) {
int count = dt != null ? dt.Rows.Count : 0;
List<T> list = new List<T>(count);
if (dt != null & dt.Rows.Count > 0)
{
foreach (DataRow row in dt.Rows)
{
list.Add(create(row));
}
}
return list;
}
然后你调用它:
var result = ConvertTo(dt, row => CreateObjectFromRow(row));