Generics help

F

Frank Rizzo

I am trying to create a parametrized generic object, but the compiler won't
let me. Is this just the way it is? The class has a constraint that says
all classes must inherit from BusinessBase, which specifies a parametrized
constructor. But c# won't let me do it.

public class DataHelper<T> where T : BusinessBase, new()
{
public static List<T> DataTableToList(DataTable dataTable)
{
List<T> listOfItems = new List<T>();

foreach (DataRow dr in dataTable.Rows)
{
T recipient = new T(dr); // **** causes an error ****
listOfItems.Add(recipient);
}

return listOfItems;
}
}
 
M

Marc Gravell

The "new()" constraint specifies a public *parameterless* constructor,
so it won't know about T(dr).

The most pragmatic approach in this case would be to add a
ReadFromRow(DataRow) method to BusinessBase (perhaps abstract, if I
understand the intent), then you could use:

T recipient = new T();
recipient.ReadFromRow(DataRow);
listOfItems.Add(recipient);

This won't help if the properties are immutable and need to be set in
the ctor. You can use reflection to invoke a specific ctor, but it
loses all compile-time checking. Of course, what you are doing is
essentially a projection; so a Converter<DataRow,T> might be handy,
then you could do

foreach(DataRow dr in dataTable.Rows) {
listOfItems.Add(converter(dr));
}

Finally, in C# 3 there is LINQ projection support on data-tables, so
something akin to (not tested):

var x = (from row in dataTable.AsEnumerable()
select new Person(row)).ToList();

When the code is this simple, it is tempting to lose the helper method
- it also means that the projection ("select") can be very focused on
the task in hand - i.e. it could use an object-initializer instead, if
needed:

var x = (from row in dataTable.AsEnumerable()
select new Person(row.Field<string>("Key")) {
Name = row.Field<string>("Name"),
DateOfBirth = row.Field<DateTime>("DOB")
}).ToList();


Marc
 
W

Walter Wang [MSFT]

Hi Frank,

Currently only parameterless or default constructor is allowed as
constructor constraint on the class type.

#.NET: More on Generics in the CLR -- MSDN Magazine, October 2003
http://msdn.microsoft.com/msdnmag/issues/03/10/NET/
<quote>
The constructor constraint makes it possible for generic code to create
instances of the type specified by a type parameter by constraining type
arguments to types that implement a public default constructor. At this
time, only default or parameterless constructors are supported with the
constructor constraint.
</quote>

#Constraints on Type Parameters (C#)
http://msdn2.microsoft.com/en-us/library/d5x73970(VS.80).aspx
<quote>
where T : new()
The type argument must have a public parameterless constructor.
</quote>

#where (C#)
http://msdn2.microsoft.com/en-us/library/6b0scde8(VS.80).aspx
<quote>
The new() Constraint lets the compiler know that any type argument supplied
must have an accessible parameterless--or default-- constructor.
</quote>


You could either create a method to pass in the parameters or use
reflection to instantiate the type.


Regards,
Walter Wang ([email protected], remove 'online.')
Microsoft Online Community Support

==================================================
When responding to posts, please "Reply to Group" via your newsreader so
that others may learn and benefit from your issue.
==================================================

This posting is provided "AS IS" with no warranties, and confers no rights.
 
T

Trey Nash

Hi Frank,

Of course, C# 3.0 and LINQ really open up some nice possibilities here for
what you are trying to do. But if you don't have LINQ at your disposal and
you're confined to C# 2.0, I have a few suggestions that may help.

First, for the line that won't compile. One of the best ways to get around
this is to externalize the creation of your T type by using a generic
delegate that returns a T and accepts a DataRow. The downside to this is
that for each concrete type that T could be, you need to create a method
that can create one of those concrete types from a DataRow and then wire the
method up to an instance of your generic delegate. Then you make that
delegate instance available to the DataTableToList() method so it can use it
to do the work you need in the line that won't compile. In C# 2.0, you can
simplify this syntax a bit by using anonymous methods, or not, depending on
your preference.

Additionally, I would recommend that you not actually return a List<T> from
DataTableToList() but rather an IEnumerable<T> instead. Then, you can turn
your DataTableToList() method into an iterator by using a yield block. This
lazy evaluation could gain you some efficiency if one were to only iterate
through the first few items of the list because you would only be calling
the delegate mentioned in the previous paragraph for the actual items
requested rather than for the whole DataTable.

Hope this helps,

-Trey
 
M

Marc Gravell

C# 3 there is LINQ projection support on data-tables
Oops; I meant .NET 3.5; subtle but important difference
row.Field<string>("Name"),
For info, this was just because my sample had an untyped DataTable; if
you have a typed DataTable, then you can:
a: lose the AsEnumerable()
b: access the properties directly

I also agree with the other poster's comment about return
IEnumerable<T> - this allows the simple

var x = from row in typedDataTable
select new Person(row.Key) { Name = row.Name, DateOfBirth =
row.DOB};

(or the equivalent using the Select(...) method)

Marc
 
D

Duy Lam

Frank said:
I am trying to create a parametrized generic object, but the compiler won't
let me. Is this just the way it is? The class has a constraint that says
all classes must inherit from BusinessBase, which specifies a parametrized
constructor. But c# won't let me do it.

public class DataHelper<T> where T : BusinessBase, new()
{
public static List<T> DataTableToList(DataTable dataTable)
{
List<T> listOfItems = new List<T>();

foreach (DataRow dr in dataTable.Rows)
{
T recipient = new T(dr); // **** causes an error ****
listOfItems.Add(recipient);
}

return listOfItems;
}
}


I think you can use a litter trick to solve (if this way don't break
your classes desgin)


public class DataHelper<T> where T : BusinessBase, new()
{
public static List<T> DataTableToList(DataTable
dataTable,FetchObjectDelegate<T> fetchObject)
{
List<T> listOfItems = new List<T>();

foreach (DataRow dr in dataTable.Rows)
{
T recipient = fetchObject(dr); // delegate create
object task to another method
listOfItems.Add(recipient);
}

return listOfItems;
}
}


And you just make new delegate for each business object.

public delegate T FetchObjectDelegate<T>(DataRow dr);

public static class FetchObjectMethods {
public static Company FetchCompany(DataRow dr) {
return new Company(dr);
}
public static Employee FetchCompany(DataRow dr) {
return new Employee (dr);
}
}
 
M

Marc Gravell

For info, that is identical to the Converter<DataRow,T> approach I
already cited, except that Converter<DataRow, T> uses a well-known
delegate.

Marc
 

Ask a Question

Want to reply to this thread or ask your own question?

You'll need to choose a username for the site, which only take a couple of moments. After that, you can post your question and our members will help you out.

Ask a Question

Similar Threads

Instantiation of generics 4
Help with generics 2
Polymorphic generics 4
generics compile error 5
about using compare with generics 8
Generics 7
Generics with multiple constrains 6
Generics Questions 10

Top