7

I want to create some instances of classes via Activator.CreateInstance(...). All classes inherit same abstract class. The constructor has one parameter.

Classes and constructors should not be public.

This is what I want in code (but not get):

internal abstract class FooAbstract
{
    protected Bar MyProperty { get; set; }

    // Constructor is only need in concreat classes of FooAbstract
    protected FooAbstract(Bar barProperty)
    {
        MyProperty = barProperty;
    }
}

internal class Foo : FooAbstract
{
    // Internal is enough, public is not necessary
    internal Foo(Bar barProperty) 
        : base(barProperty)
    {
}

// Many more Foo´s ...

internal class Creator()
{
    private object CreateAFoo<T>() where T : FooAbstract
    {
        T someFoo = (T)Activator.CreateInstance(typeof(T), barProperty);
    }
}

But this throws an Exception Constructor on type 'Foo' not found.

When I change constructor of FooAbstract and Foo to public all will be fine (classes stay internal!).

So I can understood that Activator.CreateInstance(...) needs public access (he comes from outside the package), but why is this possible with remaining internal classes?

Until now I thought that when class is internal and constructor is public it would be the same as class is internal and constructor is also internal (to kind of hierarchic access layers) ... but this seems to be wrong!

Can somebody help me to understand what happened here - why public constructors in internal classes do work?

Micha
  • 5,117
  • 8
  • 34
  • 47
  • 1
    This is a very good question - I'd love to see an explanation. You are absolutely correct that when a class is internal, then any of its constructors will also be internal even if they are marked as public. Therefore, as you say, it making it public shouldn't have any effect on Activator.CreateInstance()! – Matthew Watson Oct 15 '13 at 12:25

2 Answers2

14

You need to specify the BindingFlags for reflection to find it:

(T)Activator.CreateInstance(typeof(T),
    BindingFlags.Instance | BindingFlags.NonPublic,
    null
    new object[] { barProperty },
    null);

Now, in this case you do need to build an object[] because it's not a params.

As Matthew Watson stated, I should clarify the way reflection works. And maybe more specifically modifiers. They [modifiers] aren't built for real protection. They are built to determine the API that's available when you're using the types.

Reflection however, works directly off the modifier. If it's public - then with reflection it's public. It doesn't matter the hierarchy. Remember, reflection can actually access private members. I know, I've had to hack a few things like that before.

Further, constructors don't inherit the modifier of the class. The default constructor - that's generated by the compiler if you don't define it - is always public.

Mike Perrenoud
  • 66,820
  • 29
  • 157
  • 232
  • 1
    Yes this is how you can do it, but it doesn't answer the actual question, which is why changing the constructor to public would fix it. Constructors in internal classes should always be internal regardless of whether they are marked as public or not... – Matthew Watson Oct 15 '13 at 12:24
  • @MatthewWatson, actually reflection literally works off of the modifier. So, since the constructor was made `public` - it's `public`. Remember, modifiers aren't built for reflection or even *real* protection. Reflection can access `private` members. – Mike Perrenoud Oct 15 '13 at 12:25
  • 2
    Ah ok, understood. So the constructor is considered to be "internal" for non-reflection use, and to be "public" for reflection use. Interesting (and potentially quite confusing!) Anyway you should write that as the answer, since that's the question the OP was actually asking. – Matthew Watson Oct 15 '13 at 12:28
  • 1
    @MatthewWatson, yeah the fact that reflection doesn't actually work within the bounds of the modifier, except for the fact that the `BindingFlags` determine what it can *find*, is a powerful **and** dangerous thing. With great power comes great responsibility. – Mike Perrenoud Oct 15 '13 at 12:29
  • Also note that this means that if you have an internal class and you don't define any constructors, then the default constructor will be considered public from the point of view of reflection! Really this means that we should have a rule along the lines of "If you declare an internal class with no constructors, define an internal default constructor to avoid accidentally using the class via reflection when you only intended to use public constructors". – Matthew Watson Oct 15 '13 at 12:30
  • 2
    @MatthewWatson, fantastic additions my friend! I think I've covered what we've discussed. – Mike Perrenoud Oct 15 '13 at 12:33
  • Thanks neoistheone for create answer and thanks @MatthewWatson for create discussion. neoistheone: very nice spiderman quotation ;) – Micha Oct 16 '13 at 05:49
  • Thanks, I found this very helpful. I was trying to instantiate a class with a protected internal constructor. This example allowed me to do it. Otherwise I would have had to make the constructor public and then lose encapsulation outside the assembly – Charles Owen Apr 07 '21 at 16:31
1

The activator uses reflection to invoke the proper instance of the constructor. It likely by default is looking only for public class members. As stated by neoistheone you can change the way it is looking for the constructor by setting flags on the activator method call. The decompiled code for that method looks like this.

[SecuritySafeCritical]
[MethodImpl(MethodImplOptions.NoInlining)]
public static object CreateInstance(Type type, BindingFlags bindingAttr, Binder binder, object[] args, CultureInfo culture, object[] activationAttributes)
{
    if (type == null)
    {
        throw new ArgumentNullException("type");
    }
    if (type is TypeBuilder)
    {
        throw new NotSupportedException(Environment.GetResourceString("NotSupported_CreateInstanceWithTypeBuilder"));
    }
    if ((bindingAttr & (BindingFlags)255) == BindingFlags.Default)
    {
        bindingAttr |= (BindingFlags.Instance | BindingFlags.Public | BindingFlags.CreateInstance);
    }
    if (activationAttributes != null && activationAttributes.Length > 0)
    {
        if (!type.IsMarshalByRef)
        {
            throw new NotSupportedException(Environment.GetResourceString("NotSupported_ActivAttrOnNonMBR"));
        }
        if (!type.IsContextful && (activationAttributes.Length > 1 || !(activationAttributes[0] is UrlAttribute)))
        {
            throw new NotSupportedException(Environment.GetResourceString("NotSupported_NonUrlAttrOnMBR"));
        }
    }
    RuntimeType runtimeType = type.UnderlyingSystemType as RuntimeType;
    if (runtimeType == null)
    {
        throw new ArgumentException(Environment.GetResourceString("Arg_MustBeType"), "type");
    }
    StackCrawlMark stackCrawlMark = StackCrawlMark.LookForMyCaller;
    return runtimeType.CreateInstanceImpl(bindingAttr, binder, args, culture, activationAttributes, ref stackCrawlMark);
}

RuntimeType is a reflected type there is a stack overflow question about it here: What's the difference between System.Type and System.RuntimeType in C#?

Community
  • 1
  • 1
kicks
  • 88
  • 5