7

I have a type (Human) and I want to know if I could do System.Activator.CreateInstance(Of Human)(). So basically I want to check if Human has a public parameterless constructor / or a public constructor with optional parameters that make it possible to call New Human without giving any arguments.

Is it possible to check beforehand if System.Activator.CreateInstance(Of T) will fail? (I mean other than wrapping the statement System.Activator.CreateInstance(Of Human)() in a Try Catch of course..)

I've tried this but it doesn't work:

Option Strict On : Option Explicit On
Module Test
    Public Class Human
        Public Sub New(Optional ByVal a As Integer = 1)

        End Sub
    End Class
    Public Sub Main()
        Dim c = GetType(Human).GetConstructor(System.Type.EmptyTypes)
        MsgBox(c Is Nothing)
    End Sub
End Module
Pacerier
  • 86,231
  • 106
  • 366
  • 634
  • Note, that this will not/never work out of the box for constructors "with only optional arguments". See my answer below for more details and background. – Christian.K May 25 '11 at 12:40
  • possible duplicate of [How do I check if a type provides a parameterless constructor?](http://stackoverflow.com/questions/4681031/how-do-i-check-if-a-type-provides-a-parameterless-constructor) – nawfal Apr 23 '13 at 05:53

4 Answers4

9

To check if the constructor is empty or all paramters are optional:

var hasEmptyOrDefaultConstr = 
  typeof(Human).GetConstructor(Type.EmptyTypes) != null || 
  typeof(Human).GetConstructors(BindingFlags.Instance | BindingFlags.Public)
    .Any (x => x.GetParameters().All (p => p.IsOptional));
Magnus
  • 45,362
  • 8
  • 80
  • 118
  • this isn't working if I have a `Private sub new` he tells me its True. but if i do ` System.Activator.CreateInstance(Of Human)()` I will get an exception of course! – Pacerier May 24 '11 at 17:29
  • Ok, I've updated the answer to exclude non public and static constructors – Magnus May 24 '11 at 17:34
  • ok cool I've settled with this: `Dim is_activatable = GetType(Human).GetConstructors(System.Reflection.BindingFlags.Instance Or System.Reflection.BindingFlags.Public).Any(Function(c) c.GetParameters.All(Function(p) p.IsOptional))` – Pacerier May 24 '11 at 17:53
  • 1
    btw isn't it weird that there isn't a built in `IsActivatable` function ? – Pacerier May 24 '11 at 17:54
  • I can see how that would be useful. – Magnus May 24 '11 at 18:07
  • +1: thanks. Like the LINQ query to check for a constructor with all arguments optional – sgtz Feb 02 '12 at 15:02
4

You can check and make sure your type Human has an accessible parameterless constructor. Easiest way to do is create a generic method and add a constraint where T : new()

EDIT:

You can manually check for parameterless constructor like this

if(typeof(Human).GetType().GetConstructor(Type.EmptyTypes) !=null)
{
  ...      
}
Bala R
  • 107,317
  • 23
  • 199
  • 210
  • I do know about that constraint where T:new() however I need T to support both types that have constraint `new()` and types that do not have that constraint – Pacerier May 24 '11 at 16:59
  • @Pacerier Optional parameters makes it tricky. But @Magnus answer addressed that area. – Bala R May 24 '11 at 17:22
  • extra `GetType()` shouldn't be used as `typeof(Human)` already gets the type. (or just use `GetType()` instead of `typeof()`) – U. Bulle Oct 07 '16 at 19:52
3

Please note, that Activator.CreateInstance<T>() or Activator.CreateInstance(Type) will only work with parameterless constructors. A constructor with only optional arguments is not parameterless in that sense.

Optional parameters (or arguments) are resolved by the C# compiler on the call site. However, there is no compiler involved in such a way when invoking the constructor using reflection.

If you use the following code, you'll get a MissingMethodException saying that there is no parameterless constructor defined:

namespace ConsoleApplication1
{
    public class Foo
    {
        public Foo(int optional = 42)
        {
        }
    }

    class Program
    {
        static void Main(string[] args)
        {
            Activator.CreateInstance<Foo>(); // causes MissingMethodException
        }
    }
}

In other words optional arguments are a compiler thing, not a CLR thing.

For more information about "corner cases with optional arguments" see this recent blog series of Eric Lippert:

http://ericlippert.com/2011/05/09/optional-argument-corner-cases-part-one/

Having that said, you might be able to somehow replicate the desired behavior using some more reflection and manually creating the required parameters for the constructor call. The compiler will place some attributes inside the assembly which you could use for that purpose, I guess.

Example:

public class Foo
{
    public Foo([Optional, DefaultParameterValue(5)] int optional)
    {
        Console.WriteLine("Constructed");
    }
}
Eric Lippert
  • 647,829
  • 179
  • 1,238
  • 2,067
Christian.K
  • 47,778
  • 10
  • 99
  • 143
  • btw how do we translate your last paragraph into vb? – Pacerier May 25 '11 at 16:14
  • @Pacerier: I'm not quite sure what exactly you are referring to, but the attributes placed into the code by the compiler are the same for C# and VB.NET, i.e. both compilers seem to generate the same code in that respect. – Christian.K May 26 '11 at 04:25
  • @Christian.K I mean i don't know where to put the line **[Optional, DefaultParameterValue(5)]**. this doesn't compile for me: Public Class Foo Public Sub New([Optional,DefaultParameterValue(5)]ByVal x As Integer) End Sub End Class – Pacerier May 26 '11 at 15:36
  • @Pacerier You don't put it there, that does your compiler (VB.NET or C#). You need to *read* them (using reflection). To stay in the spirit of the other answers you could do something like this: (1) figure out if you have a true parameterless constructor, if so, do as the others suggested. (2) if you have a constructor where all paramaters have default values (hence the attributes shown in the "Example" of my answer), you use reflection to get the values, and build a proper object[] to pass it to the Activator.CreateInstance-overload for non-parameterless constructors. – Christian.K May 27 '11 at 05:23
1

I created a quick console app using reflection to test for parameter-less constructors on types. Is this what you need?

using System;
using System.Linq;

namespace ConsoleApplication
{
    public class HasPublicParameterlessContructorClass
    {
    }

    public class DoesntHavePublicParameterlessContructorClass
    {
        private int someField;

        public DoesntHavePublicParameterlessContructorClass(int someParameter)
        {
            someField = someParameter;
        }
    }

    class Program
    {
        public static bool HasPublicParameterlessContructor(Type t)
        {
            return t.GetConstructors().Any(constructorInfo => constructorInfo.GetParameters().Length == 0);
        }

        static void Main()
        {
            Console.WriteLine(HasPublicParameterlessContructor(typeof(HasPublicParameterlessContructorClass)));
            Console.WriteLine(HasPublicParameterlessContructor(typeof(DoesntHavePublicParameterlessContructorClass)));
        }
    }
}
Pedro Santos
  • 974
  • 2
  • 14
  • 23