C#泛型问题

3

我对泛型的使用有些生疏,尝试做以下操作,但编译器报错:

protected List<T> PopulateCollection(DataTable dt) where T: BusinessBase
{
    List<T> lst = new List<T>();
    foreach (DataRow dr in dt.Rows)
    {
        T t = new T(dr);
        lst.Add(t);
    }
    return lst;
}

正如您所看到的,我试图通过将DataRow传递给构造函数,将表格内容转储到对象中,然后将该对象添加到集合中。但它抱怨T不是它知道的类型或命名空间,并且我不能在非泛型声明上使用where。

这不可能吗?

6个回答

21

有两个大问题:

  • 您无法指定需要参数的构造函数约束
  • 您的方法目前不是通用的 - 它应该是PopulateCollection<T>而不是PopulateCollection

您已经有了一个约束 T : BusinessBase,所以为了解决第一个问题,我建议您在 BusinessBase 中添加一个抽象 (或虚拟) 方法:

public abstract void PopulateFrom(DataRow dr);

此外,还需要在 T 上添加一个无参数构造函数约束。
您的方法将变为:
protected List<T> PopulateCollection(DataTable dt)
    where T: BusinessBase, new()
{
    List<T> lst = new List<T>();
    foreach (DataRow dr in dt.Rows)
    {
        T t = new T();
        t.PopulateFrom(dr);
        lst.Add(t);
    }
    return lst;
}

如果您使用的是.NET 3.5,您可以使用DataTableExtensions中的扩展方法使其更加简单:
protected List<T> PopulateCollection<T>(DataTable dt)
    where T: BusinessBase, new()
{
    return dt.AsEnumerable().Select(dr => 
    { 
        T t = new T();
        t.PopulateFrom(dr);
    }.ToList();
}

或者,您可以将其作为扩展方法本身(再次假设.NET 3.5),并传递一个函数来返回实例:

static List<T> ToList<T>(this DataTable dt, Func<DataRow dr, T> selector)
    where T: BusinessBase
{
    return dt.AsEnumerable().Select(selector).ToList();
}

您的来电者会这样写道:

...

table.ToList(row => new Whatever(row));

假设您回到拥有一个接受 DataRow 的构造函数。这样做有利于编写不可变类(以及没有无参数构造函数的类),但这也意味着您不能通用地工作,除非还具有“工厂”功能。


+1 对问题的澄清,并获取最新版本。我认为在这种情况下,中间版本并不比 foreach 更简单。 - eglasius
我没有编辑权限,所以任何有权限的人,请将以下代码:return dt.Rows.AsEnumerable().Select(selector).ToList();修改为:return dt.AsEnumerable().Select(selector).ToList();因为AsEnumerable是DataTable的扩展方法,而不是.Rows集合的扩展方法。 - AngryHacker

3
唯一允许创建新实例的约束new()——基本上是一个无参构造函数。要绕过这个限制,请执行以下任意操作:
interface ISupportInitializeFromDataRow
{
    void InitializeFromDataRow(DataRow dataRow);
}

protected List<T> PopulateCollection<T>(DataTable dt) 
    where T : BusinessBase, ISupportInitializeFromDataRow, new()
{
    List<T> lst = new List<T>();
    foreach (DataRow dr in dt.Rows)
    {
        T t = new T();
        t.InitializeFromDataRow(dr);

        lst.Add(t);
    }
    return lst;
}

或者

protected List<T> PopulateCollection<T>(DataTable dt, Func<DataRow, T> builder) 
    where T : BusinessBase
{
    List<T> lst = new List<T>();
    foreach (DataRow dr in dt.Rows)
    {
        T t = builder(dr);        
        lst.Add(t);
    }
    return lst;
}

2
您可能需要在T上添加new泛型约束,如下所示:
protected List<T> PopulateCollection<T>(DataTable dt) where T : BusinessBase, new()
...

我无法将DataRow传递给构造函数,但您可以通过将其分配给BusinessBase的属性来解决这个问题。


2
一种可能的方法是:
protected List<T> PopulateCollection<T>(DataTable dt) where T: BusinessBase, new()
    {
        List<T> lst = new List<T>();
        foreach (DataRow dr in dt.Rows)
        {
            T t = new T();
            t.DataRow = dr;
            lst.Add(t);
        }
        return lst;
    }

公共类 BusinessBase{ 公共 DataRow DataRow { 获取; 设置; }} - Simon

0
where T: BusinessBase

我认为应该添加new()的限制。


0

这是可能的。我的框架中也有完全相同的东西。我曾经和你一样遇到了同样的问题,这是我解决它的方法。我会发布来自框架的相关片段。如果我没记错的话,最大的问题是需要调用无参数构造函数。

 public class Book<APClass> : Book where APClass : APBase
        private DataTable Table ; // data
        public override IEnumerator GetEnumerator()
        {                        
            for (position = 0;  position < Table.Rows.Count;  position++)           
                 yield return APBase.NewFromRow<APClass>(Table.Rows[position], this.IsOffline);
        }
   ...


  public class APBase ...
  {
    ...
    internal static T NewFromRow<T>(DataRow dr, bool offline) where T : APBase
        {

            Type t = typeof(T);
            ConstructorInfo ci;

            if (!ciDict.ContainsKey(t))
            {
                ci = t.GetConstructor(new Type[1] { typeof(DataRow) });
                ciDict.Add(t, ci);
            }
            else ci = ciDict[t];

            T result = (T)ci.Invoke(new Object[] { dr });

            if (offline)
                result.drCache = dr;    

            return result;
        }

在这种情况下,基类具有静态方法,使用接受tablerow的构造函数来实例化其派生类的对象。

网页内容由stack overflow 提供, 点击上面的
可以查看英文原文,
原文链接