Instantiation of generics

G

Guest

Hi,

I'm porting a fairly hefty n-tier implementation from .Net 1.1 -> .Net 2.0
(and fixing a few things in the process). One of the improvements I am
making is moving the existing non-generic collection code to generics using
the following template code. Basically a collection is built from a
List<type> as required (see code). There is a method called PopulateAll() in
the base collection which fill the collection with objects based on the type
T.

The question is: How do you create an instance of type T within the generic
base with a constructor passing the DataRow to the type's constructor. I am
willing to use any workaround available. The unknown bit is marked #######.

I have tried "new T(row)" but that obviously won't work as a generic is not
a straightforward instantiatable type and you can't do a "new" on it (as the
compiler kindly reminded me).

Simplified code (nasty bracket style) for demonstration...

// base object implementation
public abstract class BaseObject {
internal BaseObject(DataRow row) {
// create the object based on the datarow
}
}

// base collection implementation
public abstract class BaseObjectCollection<T> : List<T> {
public void PopulateAll() {
DataTable dt = new DataTable();
foreach (DataRow dr in dt.Rows) {
// vv What goes in here to instantiate a type of T?
Add(new #######(dr));
}
}
}

// Business object
public class Noggin : BaseObject {
}

// Business object collection
public class NogginCollection : BaseObjectCollection<Noggin> {
}

I'll buy whoever solves this a beer if they can meet me in
Farringdon/Holborn in London, UK!

Cheers,

- Chris.
 
G

Guest

Seeing as I've worked it out. Solution (albeit ugly as sin). Apparently you
can't create a generic constructor other than the default .ctor as part of
the framework so it's hacking time with reflection.

We create an instance by using reflection to call the constructor, then
setting the value with another call, which is protected so it's abstracted
away from the objects which inherit from BaseObject.

Sorted!

// base object implementation
public abstract class BaseObject {
protected void SetDataRow(DataRow row)
{
// handle datarow stuff here
}
}

// base collection implementation
public abstract class BaseObjectCollection<T> : List<T> {
public void PopulateAll() {
DataTable dt = new DataTable();
foreach (DataRow dr in dt.Rows) {
ConstructorInfo ci = typeof(T).GetConstructor(new Type[0]);
T temp = (T)ci.Invoke(null);
temp.GetType().InvokeMember(
"SetDataRow",
BindingFlags.NonPublic |
BindingFlags.Instance |
BindingFlags.InvokeMethod,
null, temp, new object[1] { dr });
Add(temp);;
}
}
}
I'll buy whoever solves this a beer if they can meet me in
Farringdon/Holborn in London, UK!

Offer closed - i'll be buying myself a pint!

Cheers,

- Chris.
 
N

Nicholas Paldino [.NET/C# MVP]

Chris,

You can simplify this code a little bit. First, since you don't need to
pass a parameter to your constructor, you can use the new constraint on the
generic to indicate that your object has a default constructor. That would
have removed the reflection code for constructing the object.

The second thing you could do is add a constraint for T, where it has to
derive from BaseObject. Combining the two gives you this declaration:

public abstract class BaseObjectCollection<T> : List<T> where T :
BaseObject, new()

This allows you to reduce your code from this:

ConstructorInfo ci = typeof(T).GetConstructor(new Type[0]);
T temp = (T)ci.Invoke(null);
temp.GetType().InvokeMember(
"SetDataRow",
BindingFlags.NonPublic |
BindingFlags.Instance |
BindingFlags.InvokeMethod,
null, temp, new object[1] { dr });

To this:

BaseObject temp = new T();
temp.SetDataRow(dr);

I'll take that pint now. =)

Hope this helps.


--
- Nicholas Paldino [.NET/C# MVP]
- (e-mail address removed)

Chris said:
Seeing as I've worked it out. Solution (albeit ugly as sin). Apparently
you
can't create a generic constructor other than the default .ctor as part of
the framework so it's hacking time with reflection.

We create an instance by using reflection to call the constructor, then
setting the value with another call, which is protected so it's abstracted
away from the objects which inherit from BaseObject.

Sorted!

// base object implementation
public abstract class BaseObject {
protected void SetDataRow(DataRow row)
{
// handle datarow stuff here
}
}

// base collection implementation
public abstract class BaseObjectCollection<T> : List<T> {
public void PopulateAll() {
DataTable dt = new DataTable();
foreach (DataRow dr in dt.Rows) {
ConstructorInfo ci = typeof(T).GetConstructor(new Type[0]);
T temp = (T)ci.Invoke(null);
temp.GetType().InvokeMember(
"SetDataRow",
BindingFlags.NonPublic |
BindingFlags.Instance |
BindingFlags.InvokeMethod,
null, temp, new object[1] { dr });
Add(temp);;
}
}
}
I'll buy whoever solves this a beer if they can meet me in
Farringdon/Holborn in London, UK!

Offer closed - i'll be buying myself a pint!

Cheers,

- Chris.
 
G

Guest

Top stuff!
BaseObject temp = new T();
temp.SetDataRow(dr);

Plus (explicit cast):

Add((T)temp);

to add to the list and it works nicely :)
I'll take that pint now. =)

If you're in London, drop an email to (e-mail address removed) and it's yours
:)
Hope this helps.

Fantastic - helps considerably. I had seen examples of this around but not
been able to fully understand them. When it is put in this context it is
perfect.

Many Thanks!

- Chris.
 
O

Oliver Sturm

Chris wrote:

Plus (explicit cast):

Add((T)temp);

Why would temp not be a T?

T temp = new T();
temp.SetDataRow(dr);
...
Add(temp);
If you're in London, drop an email to (e-mail address removed) and it's yours
:)

Hi, fellow Londoner :)



Oliver Sturm
 

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


Top