How to instantiate a generic type argument using a parameterized constructor in C #

I am trying to write a helper method that logs a message and throws an exception of the 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 could not create an instance of TException . Now I get the error β€œI cannot provide arguments when creating an instance of a parameter of typeβ€œ TException. ”I tried to create an instance using the constructor without parameters and then set the Message property, but it is read-only.

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

+20
generics c # exception
Dec 10 2018-10-10
source share
4 answers

You can use Activator.CreateInstance() (which allows you to pass arguments) to create an instance of TException . You can then create the generated 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; } 
+13
Dec 10 '10 at 16:44
source share

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

My recommendation in this case would be to create a delegate typed for the constructor for each type; a cache that delegates (usually in a static field of a generic type, for convenience) and reuses it. I can give an example later - but I can not do it with iPod;)

I suppose I made some code for this in the Jon Skeet MiscUtil library; so you can look there too.




As requested (comments), this is the way to do this - in this case using the Expression API. Pay attention, in particular, to the use of nested generic classes, which guarantee that we will do reflection / compilation no more than 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)); } } 
+8
Dec 10 2018-10-10
source share

This is a limitation of the general new constraint. It can only be used to create objects through the constructor without parameters.

One way around this is to provide a lambda factory method that takes the appropriate parameters. On the call site, it may refer to the class constructor

 private void LogAndThrow<TException>( Func<string,TException> func, string message, params object[] args) where TException : Exception { message = string.Format(message, args); Logger.Error(message); throw func(message); } LogAndThrow(msg => new InvalidOperationException(msg), "my message"); 
+4
Dec 10 '10 at 16:44
source share

try it

 private void ThrowAndLog<TException>(string message, params object[] args) where TException : Exception, new() { message = string.Format(message, args); Logger.Error(message); throw (TException)typeof(TException).GetConstructor( new[] { typeof(string) }).Invoke(new object[] { message }); } 
+1
Dec 10 2018-10-10
source share



All Articles