Reputation: 61646
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?
Upvotes: 3
Views: 850
Reputation: 1503529
There are two big problems:
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.
Upvotes: 21
Reputation: 9056
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.
Upvotes: 0
Reputation: 1717
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;
}
Upvotes: 2
Reputation: 8141
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
Upvotes: 2
Reputation: 115867
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;
}
Upvotes: 3
Reputation: 65555
where T: BusinessBase
Should have have restriction of new() I think added
Upvotes: 0