Peter Trevor
Peter Trevor

Reputation: 125

Generics without new()

I have a dozen methods in my project (C# 2.0) that look like this:

internal bool ValidateHotelStayEntity(DataRow row)
{
    return (new HotelStayEntity(row)).Validate();
}

... but for different 'entity' classes. (Okay, not quite that trivial, I've simplified the code here.)

It looks like a good candidate for generics and I came up with this:

internal bool ValidateEntity<T>(DataRow row) where T : EntityBase
{
    return (new T(row)).Validate();
}

And of course I get the "Cannot create an instance of the type parametrer 'T' because it does not have the new() constraint" error.

The problem is that these 'entity' classes do not have a public parameterless constructor, nor a way of adding the 'row' data in afterwards. And as EntityBase is part of the company framework I have no control over it (ie I can't change it).

Is there any way around this?

Upvotes: 7

Views: 381

Answers (4)

Jon Skeet
Jon Skeet

Reputation: 1500725

One simple way is to provide a factory function:

internal bool ValidateEntity<T>(DataRow row, Func<DataRow, T> factory)
    where T : EntityBase
{
    return factory(row).Validate();
}

and call with:

bool valid = ValidateEntity(row, x => new Foo(x));

Mind you, at that point it's more complicated than just calling

bool valid = new Foo(row).Validate()

in the first place...

It's not really clear what you're trying to achieve in your real context, but this sort of factory/provider approach can certainly be useful at other times. Note that calling a factory delegate can also be considerably faster than using new T() with a constraint, as I blogged a while ago. Irrevelant in many cases, but worth knowing about.

EDIT: For .NET 2.0 compatibility you'd need to declare the delegate yourself, but that's easy:

public delegate TResult Func<T, TResult>(T input);

If you're really using C# 2 (rather than, say, C# 3 targeting .NET 2.0) then you won't be able to use lambda expressions either, but you can still use anonymous methods:

bool valid = ValidateEntity(row, delegate(DataRow x) { return new Foo(x); });

Upvotes: 15

Alex Mendez
Alex Mendez

Reputation: 5150

You can do something like this:

internal bool ValidateEntity<T>(DataRow row) where T : EntityBase 
{
     return ((T)Activator.CreateInstance(typeof(T), new object[] { row })).Validate(); 
} 

Upvotes: 1

vc 74
vc 74

Reputation: 38179

Yet another way could be to involve reflection at the price of compile time checking and decreased performance:

internal bool ValidateEntity<T>(DataRow row)
{ 
   object entity = Activator.CreateInstance(typeof(T), new object[] { row });
   MethodInfo validate = typeof(T).GetMethod("Validate");
   return (bool) validate.Invoke(entity, new object[]);
}

Note that this would work even if the entities do not have a common ancestor

Upvotes: 3

Chris Shain
Chris Shain

Reputation: 51339

An example of @JohnSaunders' factory method solution:

internal bool ValidateEntity<T>(DataRow row, Func<DataRow, T> factory) where T : EntityBase
{
    return (factory(row)).Validate();
}

Upvotes: 1

Related Questions