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.)
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.
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.
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; }
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));
}
}
If you love us? You can donate to us via Paypal or buy me a coffee so we can maintain and grow! Thank you!
Donate Us With