How to get the ConstructorInfo of a Ctor with params

3.1k views Asked by At

I am trying to write code that receives an argument list and gets the ConstructorInfo of a matching Ctor.

The method signature is ConstructorInfo GetConstructorInfo(Type type, object[] args).

I have created a class to work with:

public class ClassWithParamsInCtor
{
    public ClassWithParamsInCtor(params int[] parameters)
    {

    }
}

Using the Activator class I can create instances of this object:

ClassWithParamsInCtor myclass = Activator.CreateInstance(typeof(ClassWithParamsInCtor), new object[] { 1,2 }) as ClassWithParamsInCtor; \\returns a valid instance of the class;

But when I try to get the ConstructorInfo there is an issue, the following returns null:

ConstructorInfo ctorInfo = typeof(ClassWithParamsInCtor).GetConstructor(new Type[] { typeof(int), typeof(int) }); \\returns null

How can I obtain the ConstructorInfo in such a case ?

3

There are 3 answers

1
Berkay Yaylacı On

Try this,

ConstructorInfo ctorInfo = typeof(ClassWithParamsInCtor).GetConstructor(new Type[] { typeof(int[])}); 

Hope helps,

4
Selman Genç On

params int[] is a syntactic sugar for int[], you need to use typeof(int[])

typeof(ClassWithParamsInCtor).GetConstructor(new Type[] { typeof(int[]) });
0
Radu Olteanu On

I think that I have a solution for your problem if I understood it right.

Assuming you have the following class :

 public class ClassWithParamsInCtor
    {
        public ClassWithParamsInCtor(params int[] parameters)
        {

        }
        public ClassWithParamsInCtor(int[] parameters, double y)
        {

        }

        public ClassWithParamsInCtor(int[] parameters, float y)
        {

        }
    }

Here are some Unit Tests that describe the solution that I've implemented :

 [TestClass]
    public class TestClassWithParamsInCtorClass
    {
        [TestMethod]
        //Wrong data type test
        public void WrongInputDataTypesTest()
        {
            //Arrange
            var inputData = new object[] { new[] { 1, 2 }, "" };
            var inputDataTypes = inputData.Select(_ => _.GetType());

            //Act
            var matchedCtorParams = typeof(ClassWithParamsInCtor)
                                   .GetConstructors()
                                   .Select(_ => _.GetParameters().Select(a => a.ParameterType))
                                   .Where(_ => _.HaveSameItems(inputDataTypes)).ToArray();

            //Assert 
            Assert.AreEqual(null, matchedCtorParams.FirstOrDefault());
        }

        [TestMethod]
        //Test used to invoke first constructor
        public void InputDataTypesTest1()
        {
            //Arrange
            var inputData = new object[] { new[] { 1, 2 } , 1 };
            var inputDataTypes = inputData.Select(_ => _.GetType());

            //Act
            var matchedCtorParams = typeof(ClassWithParamsInCtor)
                                   .GetConstructors()
                                   .Select(_ => _.GetParameters().Select(a => a.ParameterType))
                                   .Where(_ => _.HaveSameItems(inputDataTypes)).ToArray();

           var result = typeof(ClassWithParamsInCtor).GetConstructor(matchedCtorParams.FirstOrDefault().ToArray())
                   .Invoke(inputData);

            //Assert 
            Assert.AreNotEqual(null, result);

        }

        [TestMethod]
        //Test used to invoke second constructor
        public void InputDataTypesTest2()
        {
            //Arrange
            var inputData = new object[] { new[] { 1, 2 }, 1.2 };
            var inputDataTypes = inputData.Select(_ => _.GetType());

            //Act
            var matchedCtorParams = typeof(ClassWithParamsInCtor)
                                   .GetConstructors()
                                   .Select(_ => _.GetParameters().Select(a => a.ParameterType))
                                   .Where(_ => _.HaveSameItems(inputDataTypes)).ToArray();

            var result = typeof(ClassWithParamsInCtor).GetConstructor(matchedCtorParams.FirstOrDefault().ToArray())
                    .Invoke(inputData);

            //Assert 
            Assert.AreNotEqual(null, result);

        }

        [TestMethod]
        //Test used to invoke third constructor
        public void InputDataTypesTest3()
        {
            //Arrange
            var inputData = new object[] { new[] { 1, 2 }, 3.5F };
            var inputDataTypes = inputData.Select(_ => _.GetType());

            //Act
            var matchedCtorParams = typeof(ClassWithParamsInCtor)
                                   .GetConstructors()
                                   .Select(_ => _.GetParameters().Select(a => a.ParameterType))
                                   .Where(_ => _.HaveSameItems(inputDataTypes)).ToArray();

            var result = typeof(ClassWithParamsInCtor).GetConstructor(matchedCtorParams.FirstOrDefault().ToArray())
                    .Invoke(inputData);

            //Assert 
            Assert.AreNotEqual(null, result);

        }
    }

And the extension method that I used :

 public static bool HaveSameItems<T>(this IEnumerable<T> a, IEnumerable<T> b)
        {
            var dictionary = a.GroupBy(x => x).ToDictionary(g => g.Key, g => g.Count());
            foreach (var item in b)
            {
                int value;
                if (!dictionary.TryGetValue(item, out value))
                {
                    return false;
                }
                if (value == 0)
                {
                    return false;
                }
                dictionary[item] -= 1;
            }
            return dictionary.All(x => x.Value == 0);
        }

I'm pretty sure that the Unit tests can be implemented to be more granular specific, but considering this as a proofe of concept, I find them decent.