简体   繁体   中英

C# Generics question

I am a bit rusty on generics, trying to do the following, but the compiler complains:

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;
}

So as you can see, i am trying to dump contents of a Table into an object (via passing a DataRow to the constructor) and then add the object to collection. it complains that T is not a type or namespace it knows about and that I can't use where on a non-generic declaration.

Is this not possible?

There are two big problems:

  • You can't specify a constructor constraint which takes a parameter
  • Your method isn't currently generic - it should be PopulateCollection<T> instead of PopulateCollection .

You've already got a constraint that T : BusinessBase , so to get round the first problem I suggest you add an abstract (or virtual) method in BusinessBase :

public abstract void PopulateFrom(DataRow dr);

Also add a parameterless constructor constraint to T .

Your method can then become:

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;
}

If you're using .NET 3.5, you can make this slightly simpler using the extension method in 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();
}

Alternatively, you could make it an extension method itself (again, assuming .NET 3.5) and pass in a function to return instances:

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

Your callers would then write:

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

This assumes you go back to having a constructor taking a DataRow . This has the benefit of allowing you to write immutable classes (and ones which don't have a parameterless constructor) it but does mean you can't work generically without also having the "factory" function.

The only constraint you can specify which allows for creation of new instances is new() - basically, a parameterless constructor. To circumvent this do either:

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;
}

Or

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;
}

You probably need to add the new generic constraint on T, as follows:

protected List<T> PopulateCollection<T>(DataTable dt) where T : BusinessBase, new()
...

I can't pass a DataRow into the constructor, but you can solve that by assigning it to a property of BusinessBase

A possible way is:

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;
    }
where T: BusinessBase

应该有新的限制()我想补充

It is possible. I have exactly the same thing in my framework. I had exactly the same problem as you and this is how I solved it. Posting relevant snippets from the framework. If I remember correclty, the biggest problem was requirement to call parameterless constructor.

 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;
        }

In this scenario, base class has static method to instantiate objects of its derived classes using constructor that accepts tablerow.

The technical post webpages of this site follow the CC BY-SA 4.0 protocol. If you need to reprint, please indicate the site URL or the original address.Any question please contact:yoyou2525@163.com.

 
粤ICP备18138465号  © 2020-2024 STACKOOM.COM