Logo Questions Linux Laravel Mysql Ubuntu Git Menu
 

How to create an instance of a generic type argument using a parameterized constructor in C#

Tags:

I'm trying to write a helper method that would log a message and throw an exception of a specified type with the same message. I have the following:

private void LogAndThrow<TException>(string message, params object[] args) where TException : Exception, new()
{
    message = string.Format(message, args);
    Logger.Error(message);
    throw new TException(message);
}

Before adding the new() constraint the compiler complained that without it I can't instantiate TException. Now the error message I get is "Cannot provide arguments when creating an instance of a type parameter 'TException'". I tried creating the instance with the parameterless constructor and then set the Message property but it's read-only.

Is this a limitation of the language or is there a solution I don't know about? Maybe I could use reflection but that's overkill for such a simple task. (And pretty ugly, but that's a matter of personal opinion.)

like image 289
neo2862 Avatar asked Dec 10 '10 16:12

neo2862


People also ask

Can you create instances of generic type parameters?

Yes, this is nice especially if the generic class is abstract, you can do this in the concrete subclasses :) @TimKuipers The <E> in class Foo<E> is not bound to any particular type.

How do I create an instance in C sharp?

You declare an instance constructor to specify the code that is executed when you create a new instance of a type with the new expression. To initialize a static class or static variables in a non-static class, you can define a static constructor.


2 Answers

You can use Activator.CreateInstance() (which allows you to pass in arguments) to create an instance of TException. Then, you could throw the created TException.

For example:

private void LogAndThrow<TException>(string message, params object[] args) where TException : Exception, new() {     message = string.Format(message, args);     Logger.Error(message);      TException exception = (TException)Activator.CreateInstance(typeof(TException), message);     throw exception; } 
like image 114
Donut Avatar answered Oct 10 '22 23:10

Donut


Yes, that is a limitation; there is no language construct for that.

My recommendation in this case would be to create a typed delegate to the constructor per-type; cache that delegate (usually in a static field of a generic type, for convenience) and re-use it. I can provide an example later - but I can't do it from iPod ;)

I believe I committed some code for this into Jon Skeet's MiscUtil library; so you could look there too.


As requested (comments), here is a way of doing this - in this case using the Expression API. Note in particular the use of the nested generic classes that ensure we do the reflection / compilation at most once per type-combination:

using System;
using System.Linq.Expressions;

class Program {
    static void Main() {
        var ctor = TypeFactory.GetCtor<int, string, DemoType>();

        var obj = ctor(123, "abc");
        Console.WriteLine(obj.I);
        Console.WriteLine(obj.S);
    }
}

class DemoType {
    public int I { get; private set; }
    public string S { get; private set; }
    public DemoType(int i, string s) {
        I = i; S = s;
    }
}

static class TypeFactory {
    public static Func<T> GetCtor<T>() { return Cache<T>.func; }
    public static Func<TArg1, T> GetCtor<TArg1, T>() { return Cache<T, TArg1>.func; }
    public static Func<TArg1, TArg2, T> GetCtor<TArg1, TArg2, T>() { return Cache<T, TArg1, TArg2>.func; }
    private static Delegate CreateConstructor(Type type, params Type[] args) {
        if(type == null) throw new ArgumentNullException("type");
        if(args ==  null) args = Type.EmptyTypes;
        ParameterExpression[] @params = Array.ConvertAll(args, Expression.Parameter);
        return Expression.Lambda(Expression.New(type.GetConstructor(args), @params), @params).Compile();

    }
    private static class Cache<T> {
        public static readonly Func<T> func = (Func<T>)TypeFactory.CreateConstructor(typeof(T));
    }
    private static class Cache<T, TArg1> {
        public static readonly Func<TArg1, T> func = (Func<TArg1, T>)TypeFactory.CreateConstructor(typeof(T), typeof(TArg1));
    }
    private static class Cache<T, TArg1, TArg2> {
        public static readonly Func<TArg1, TArg2, T> func = (Func<TArg1, TArg2, T>)TypeFactory.CreateConstructor(typeof(T), typeof(TArg1), typeof(TArg2));
    }
}
like image 45
Marc Gravell Avatar answered Oct 10 '22 23:10

Marc Gravell