2

I have 2 methods as following:

Method1(int a, int b)
{
    var type = Typ1(a, b);
}

Method2
{
    var type = Typ2(a, b);
}

I'd like to write a generic method which does the work:

GenericMethod<T>(int a, int b)
{
    var type = new T(a, b);
}

But T doesn't accept any input parameter. How could I achieve this?

I know using Activator.Instance(T, a, b) I can do that but it has a high performance cost.

I also know that I can call the default constructor of a generic type using T() then setting the properties,but in my case, I'd like to pass 2 parameters which are compulsory.

I don't want to introduce a constructor with no parameter.

Is there any way to do this with generics?

Thanks,

The Light
  • 26,341
  • 62
  • 176
  • 258
  • 1
    It should be noted that even if you do `new T()` on a generic type which was a `new()` constraint, the compiler will simply transform that to an `Activator.CreateInstance()` call, so there is no performance benefit. The only way around that is to use some kind of factory pattern, similar to what SLaks is doing with the delegate. – Sven Jun 16 '11 at 15:08

5 Answers5

4

Create your factory class:

    public static class TypeFactory<T>
{
    private static Func<int, int, T> Func { get; set; }

    static TypeFactory()
    {
        TypeFactory<Type1>.Func = (a, b) => new Type1(a, b);
        TypeFactory<Type2>.Func = (a, b) => new Type2(a, b);
    }

    public static T Create(int a, int b)
    {
        return Func(a, b);
    }
}

Then use it like this:

        var type1 = TypeFactory<Type1>.Create(1, 2);
        var type2 = TypeFactory<Type2>.Create(1, 2);
The Light
  • 26,341
  • 62
  • 176
  • 258
  • You shouldn't initialize it in the class itself. Your initializer will run once per type parameter. – SLaks Jun 17 '11 at 01:38
  • what would you suggest then? I can use a lock inside the class to make sure the static constructor only runs once but I doubt it will gain any performance benefit over Activator.CreateInstance()? – The Light Jun 17 '11 at 07:57
  • have you got a code sample for this? I can't just move the static ctor out of the class. I mean I can move it but when should it be called and from where? I'd need it to be called only once throughout the application. I can put it in the Application_Start event! – The Light Jun 17 '11 at 19:24
2

No.

Instead, you can accept a delegate that creates them for you:

GenericMethod<T>(int a, int b, Func<int, int, T> creator) {
    T t = creator(a, b);
}

GenericMethod(8, 9, (a, b) => new YourType(a, b));

You could also store these creators in a generic static class:

static class Creator<T> {
    public static Func<int, int, T> Func { get; set; }
}
GenericMethod<T>(int a, int b) {
    T t = Creator<T>.Func(a, b);
}


Creator<YourType>.Func = (a, b) => new YourType(a, b);
Community
  • 1
  • 1
SLaks
  • 868,454
  • 176
  • 1,908
  • 1,964
  • I quite like the static generic class approach, thanks. Does it have the highest performance in comparison with other approaches? – The Light Jun 16 '11 at 22:30
2

If you don't want to use Activator, you could use an expression tree. Incorrect number of parameters supplied for lambda declaration

Community
  • 1
  • 1
Trent
  • 2,122
  • 1
  • 24
  • 38
  • 1
    To make that worth doing, you need to store the compiled expression tree (eg, in a static generic dictionary, as in my answer) so that you can reuse it later. Otherwise, it will end up being even slower. – SLaks Jun 16 '11 at 15:27
  • http://bloggingabout.net/blogs/vagif/archive/2010/04/02/don-t-use-activator-createinstance-or-constructorinfo-invoke-use-compiled-lambda-expressions.aspx Is this approach the fastest one? – The Light Jun 17 '11 at 06:39
0

In theory, you need to use a generic type constraint. However, the only constructor constraint available is support for a parameterless constructor where T : new().

If Typ1 and Typ2 share a base class which defines properties using the 2 integers or both support an interface guaranteeing setters for those integers you could define a parameterless constructor on each class and use an additional constraint to allow later access to the properties.

Raeeyesis
  • 91
  • 3
  • A constraint of T to the base type would not open up the constructors on T. For one, there is no guarantee that for a base with a parameterized constructor that the derived children *also* provide parameterized constructors. – Anthony Pegram Jun 16 '11 at 15:11
  • Even with a `new()` constraint, the compiler just translates that into a call to `Activator.CreateInstance()`, so there's no performance gain. The only thing you gain is compile time checking for the presence of a constructor. – Sven Jun 16 '11 at 15:17
  • @Sven, where do you get that? I see no such evidence in the IL or the C# language specification on the constraint. – Anthony Pegram Jun 16 '11 at 15:23
  • I'm not sure if it's required behaviour by the specification, but it is what both the Microsoft and Mono C# compilers do; you can check it with Reflector of ILSpy or something similar. – Sven Jun 16 '11 at 15:26
  • @Sven, SLaks, You know what, nevermind. I tested the wrong method. I see the IL on `return new T()` and `return Activator.CreateInstance()` is the same. `return (T)Activator.CreateInstance(typeof(T))` is markedly different. – Anthony Pegram Jun 16 '11 at 15:30
0
public static class MyTypeFactory
{
    static MyTypeFactory()
    {
        MethodRunner<Type1>.Func = (a, b) => new Type1(a, b);
        MethodRunner<Type2>.Func = (a, b) => new Type2(a, b);
    }

    public static T Create<T>(int a, int b)
    {
        return MethodRunner<T>.Func(a, b);
    }

    static class MethodRunner<T>
    {
        public static Func<int, int, T> Func { get; set; }
    }
}

This looks promising?!

is a static ctor thread-safe by nature (CLR) like static field initializers?

The Light
  • 26,341
  • 62
  • 176
  • 258