简体   繁体   中英

How to get default constructor when parameters are optional

I'm using Type.GetConstructor(Type.EmptyTypes) to get the default constructor for a class. It works if the class has a default constructor with no parameters ( class A ). But it doesn't work if a class has a constructor with all parameters optional ( class B ). Program doesn't know what the optional parameters are because it only needs the default constructor. What can statements can I use to make it work for both cases? Thanks, appreciate any help!

public class A
{
    public A() {}
} 

public class B
{
    public B(int i = 0, string str = "") {}
}

Say I have the following class:

public class SomeClass
{
    public SomeClass()
    {

    }

    public SomeClass(int x)
    {
    }

    public SomeClass(int x = 0, int y = 0)
    {

    }
}

Basically, you're asking for a query that will find the constructors that match constructor 1 and 3 above? If so, use this:

var constuctors = typeof(SomeClass).GetConstructors()
            .Where(x => x.GetParameters().Count() == 0 
                    ||  x.GetParameters().Count(param => param.GetCustomAttributes(typeof(OptionalAttribute), false).Count() > 0) == x.GetParameters().Count());    

Incredibly nasty query, but it gets the job done returning only 1 and 3 above.

The problem is that optional parameters are nothing more than a compile time concept. You'll need to specify the constructor completely.

var ci = typeof(B).GetConstructor(new [] { typeof(int), typeof(string) });

You can write a help function that will invoke the constructor with the default values though. My example is not as robust as it should be but it should get you started.

static Func<T> CreateDefaultConstructor<T>(ConstructorInfo ci)
{
    var l = new List<object>();
    foreach (var p in ci.GetParameters())
    {
        if (p.IsOptional)
        {
            l.Add(p.RawDefaultValue);
        }
    }
    return () => (T)ci.Invoke(l.ToArray());
}

The problem is that the C# compiler produces this:

public class B
{
    // Methods
    public B([Optional, DefaultParameterValue(0)] int i, [Optional, DefaultParameterValue("")] string str)
    {
    }
}

Something like below should work:

public static class TypeHelper {
    public static ConstructorInfo GetDefaultConstructor<TType>() {
        var type = typeof(TType);
        return type.GetDefaultConstructor();
    }

    public static ConstructorInfo GetDefaultConstructor(this Type type) {
        if(type == null) throw new ArgumentNullException("type");
        var constructor = type.GetConstructor(Type.EmptyTypes);
        if(constructor == null) {
            var ctors = 
                from ctor in type.GetConstructors()
                let prms = ctor.GetParameters()
                where prms.All(p=>p.IsOptional)
                orderby prms.Length
                select ctor;                        
            constructor = ctors.FirstOrDefault();
        }
        return constructor;
    }
}

The problem is that, in the case of B, it does not have a constructor with no parameters.

Optional arguments are a compile time construct - in the IL, it's a constructor with 2 parameters (which are flagged with attributes). As such, there is no default constructor as far as Reflection is concerned.

To get the one that has more optional parameters or an empty constructor at all, use:

typeof(myClass)
.GetConstructors()
.OrderBy(x => x.GetParameters().Length - x.GetParameters().Count(p => p.IsOptional))
.FirstOrDefault();

When a constructor, or any other method, has optional arguments it doesn't cause the compiler to generate multiple versions of the method. Instead it generates a single method which has all of the specified parameters. The default values are encoded in attributes attached to the method signature. These are used at the call site to make their values optional.

So here there is no default constructor but instead a single one with 2 parameters

The technical post webpages of this site follow the CC BY-SA 4.0 protocol. If you need to reprint, please indicate the site URL or the original address.Any question please contact:yoyou2525@163.com.

 
粤ICP备18138465号  © 2020-2024 STACKOOM.COM