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?
-
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
-
where T: BusinessBase
Should have have restriction of new() I think added
-
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 ofPopulateCollection
.
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 inBusinessBase
: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.eglasius : +1 clear on the issues, and for the last version. I don't think the intermediate version is much simpler than the foreach in this case.AngryHacker : I don't have the power to edit, so anyone who can, change return dt.Rows.AsEnumerable().Select(selector).ToList(); to return dt.AsEnumerable().Select(selector).ToList(); since AsEnumerable is an extention method on the DataTable not on the .Rows collection.Jon Skeet : @AngryHacker: Thanks, done. -
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; }
-
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; }
Simon : public class BusinessBase{ public DataRow DataRow { get; set; }} -
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.
0 comments:
Post a Comment
Note: Only a member of this blog may post a comment.