Internal classes and public constructors - why does it work with Activator.CreateInstance?
Asked Answered
D

2

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?

Duster answered 15/10, 2013 at 12:2 Comment(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()!Coltun
F
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.

Fouquiertinville answered 15/10, 2013 at 12:9 Comment(8)
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...Coltun
@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.Fouquiertinville
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.Coltun
@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.Fouquiertinville
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".Coltun
@MatthewWatson, fantastic additions my friend! I think I've covered what we've discussed.Fouquiertinville
Thanks neoistheone for create answer and thanks @MatthewWatson for create discussion. neoistheone: very nice spiderman quotation ;)Duster
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 assemblyGift
B
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#?

Bravery answered 15/10, 2013 at 12:29 Comment(1)
Thanks kicks for additional informations - very interesting. +1 for that.Duster

© 2022 - 2024 — McMap. All rights reserved.