12

I am working on a C# generic function. When error, if the generic type can be new-able, return new T(), otherwise return default(T).

The code like this:

private T Func<T>()
{
    try
    {
        // try to do something...
    }
    catch (Exception exception)
    {
        if (T is new-able) // <---------- How to do this?
        {
            return new T();
        }
        else
        {
            return default(T);
        }
    }
}

I know it needs where T : new() for those using new T(). This question is, how to judge this on runtime?

svick
  • 214,528
  • 47
  • 357
  • 477
Junle Li
  • 1,025
  • 1
  • 11
  • 18
  • 7
    Although achievable using reflection, this indicates a rather smelly convention for callers of that method IMHO. Can you describe a bit better what exactly is that you're trying to do? – Groo Aug 08 '14 at 06:53
  • 1
    Unless I'm mistaken can you not just use `return Activator.CreateInstance(T)`? – Sayse Aug 08 '14 at 06:53
  • @Sayse No, that will throw exception if no parameterless constructor defined. – Sriram Sakthivel Aug 08 '14 at 06:54
  • @SriramSakthivel - Judging by the OP's example, I presumed that would be fine, but thanks – Sayse Aug 08 '14 at 06:55
  • 4
    This seems like a really bad design. Even then, your code doesn't seem to implement your design goals: you seem to want to absolutely never let any thrown exception be visible outside of your function. But what if `new T()` throws? –  Aug 08 '14 at 07:06
  • @hvd The basic idea is to avoid try-catch boilerplate in caller function. If return `default(T)` when `T` a list, it is not safe to chain in the caller. For those `T` is a list or my model, I can assert it's safe to do `new T()`. – Junle Li Aug 08 '14 at 07:13

4 Answers4

20

You just need to check whether the type has a parameterless constructor. You do it by callingType.GetConstructor method with empty types as parameter.

var constructorInfo = typeof(T).GetConstructor(Type.EmptyTypes);
if(constructorInfo != null)
{
   //here you go
   object instance = constructorInfo.Invoke(null);
}
Sriram Sakthivel
  • 67,773
  • 7
  • 96
  • 172
7

If I remember correctly, Activator.CreateInstance<T> will return an object constructed with the parameterless constructor if T is a class or a default(T) if T is a struct.

You can use the technique in Sriram's answer to first make sure a parameterless constructor exists for T.

Theodoros Chatzigiannakis
  • 26,988
  • 8
  • 61
  • 97
  • 4
    If no parameterless constructor, it will throw exception. Isn't it? – Sriram Sakthivel Aug 08 '14 at 06:53
  • in that case you can still catch the exception and return default(T)... not perfectly performant but otherwise ok. – BatteryBackupUnit Aug 08 '14 at 06:59
  • @BatteryBackupUnit - Exceptions should be there to catch the exceptional, not for program flow – Sayse Aug 08 '14 at 07:01
  • @Sayse well i agree in general but not necessarily for this specific example. I think it's ok to define such rules in your team / company, but postulating this as a "hard rule" without explaining it in detail is basically falling back to a (kind of fanatic) behavior which was somewhat obsolete since the age of enlightment. As for the topic at hand http://stackoverflow.com/questions/729379/why-not-use-exceptions-as-regular-flow-of-control contains quite some information. – BatteryBackupUnit Aug 08 '14 at 08:55
1

You could something like checking for a default constructor and execute new T() if one is found. To do this you could use something like:

var constructor = typeof(T).GetConstructor(Type.EmptyTypes);
if(constructor != null)
{
    return (T)constructor.Invoke(null);
}
else
{
    return default(T);
}
MarcoLaser
  • 266
  • 1
  • 5
1

I little played with MarcoLaser's answer and made my 'if T is newable then get instance constructor'

public class MyClass<T>
{
        public MyClass()
        {
            var constructor = typeof(T).GetConstructor(Type.EmptyTypes);
            if (constructor != null)
                Data = (T)constructor.Invoke(null);  
        }

        public T Data { get; set; }
}