Logo Questions Linux Laravel Mysql Ubuntu Git Menu
 

Generics without new()

Tags:

c#

generics

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?

like image 484
Peter Trevor Avatar asked Jan 26 '12 19:01

Peter Trevor


People also ask

What does where T new () mean?

where T : new() Means that the type T must have a parameter-less constructor. Having this constraint will allow you to do something like T field = new T(); in your code which you wouldn't be able to do otherwise. You then combine the two using a comma to get: where T : class, new()

Why should we avoid generics in Java?

Many people are unsatisfied with the restrictions caused by the way generics are implemented in Java. Specifically, they are unhappy that generic type parameters are not reified: they are not available at runtime. Generics are implemented using erasure, in which generic type parameters are simply removed at runtime.

What is the disadvantages of using generics?

According to oracle documentation, the following points are the disadvantage of generics: Cannot instantiate Generic types with primitive types. Cannot create instances of type parameters. Cannot declare static fields whose types are type parameters.


3 Answers

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); });
like image 130
Jon Skeet Avatar answered Oct 22 '22 18:10

Jon Skeet


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

like image 42
vc 74 Avatar answered Oct 22 '22 18:10

vc 74


An example of @JohnSaunders' factory method solution:

internal bool ValidateEntity<T>(DataRow row, Func<DataRow, T> factory) where T : EntityBase
{
    return (factory(row)).Validate();
}
like image 1
Chris Shain Avatar answered Oct 22 '22 19:10

Chris Shain