DataTable到List <t> *没有*其中T:class,new() - 潜在的问题?</t>

时间:2009-11-24 12:08:06

标签: c# generics

我今天早上一直在寻找一种从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呼叫是不必要的......

4 个答案:

答案 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));