内部类和公共构造函数 - 为啥它与 Activator.CreateInstance 一起工作?

Posted

技术标签:

【中文标题】内部类和公共构造函数 - 为啥它与 Activator.CreateInstance 一起工作?【英文标题】:Internal classes and public constructors - why does it work with Activator.CreateInstance?内部类和公共构造函数 - 为什么它与 Activator.CreateInstance 一起工作? 【发布时间】:2013-10-23 05:27:50 【问题描述】:

我想通过Activator.CreateInstance(...) 创建一些类的实例。所有类都继承相同的抽象类。构造函数只有一个参数。

类和构造函数不应公开。

这是我想要的代码(但不是得到):

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);
    

但这会引发异常Constructor on type 'Foo' not found

当我将FooAbstract Foo 的构造函数更改为public 时,一切都会好起来的(类保持internal!)。

所以我可以理解Activator.CreateInstance(...) 需要公共访问(他来自包外),但是为什么剩下的内部类可以做到这一点?

直到现在我认为当 class 是 internal 并且 constructor is public 它会和 class is internal 构造函数也是内部的(对于某种分层访问层)......但这似乎是错误的!

有人可以帮我理解这里发生了什么 - 为什么内部类中的公共构造函数可以工作?

【问题讨论】:

这是一个非常好的问题 - 我很想看到一个解释。你是绝对正确的,当一个类是内部的,那么它的任何构造函数也将是内部的,即使它们被标记为公共的。因此,正如您所说,公开它不应该对 Activator.CreateInstance() 产生任何影响! 【参考方案1】:

您需要指定BindingFlags 进行反射才能找到它:

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

现在,在这种情况下,您确实需要构建object[],因为它不是params

正如 Matthew Watson 所说,我应该澄清反射的工作方式。也许更具体地说是修饰符。它们 [修饰符] 不是为真正的保护而构建的。它们用于确定在您使用这些类型时可用的 API。

但是,反射直接作用于修饰符。如果是public - 那么通过反射它是public。等级无关紧要。请记住,反射实际上可以访问private 成员。我知道,我以前不得不破解一些类似的东西。

此外,构造函数不继承class 的修饰符。默认构造函数 - 如果您没有定义它,则由编译器生成 - always public.

【讨论】:

是的,你可以这样做,但它不能回答实际问题,这就是为什么将构造函数更改为 public 会解决它的原因。内部类中的构造函数应该始终是内部的,无论它们是否被标记为公共...... @MatthewWatson,实际上反射实际上是通过修饰符起作用的。所以,由于构造函数是public - 它是public。请记住,修饰符不是为反射甚至是真正的保护而构建的。反射可以访问private成员。 嗯,好的,明白了。因此,构造函数被认为是“内部”的非反射使用,而被认为是“公共”的反射使用。有趣(并且可能非常令人困惑!)无论如何,您应该将其写为答案,因为这是 OP 实际提出的问题。 @MatthewWatson,是的,除了 BindingFlags 确定它可以找到的内容之外,反射实际上并不能在修饰符的范围内工作,是一个强大的危险的东西。权力越大,责任越大。 @MatthewWatson,我的朋友,很棒的补充!我想我已经涵盖了我们讨论过的内容。【参考方案2】:

激活器使用反射来调用构造函数的正确实例。默认情况下,它可能只寻找公共类成员。正如 neoistheone 所述,您可以通过在 activator 方法调用上设置标志来更改它寻找构造函数的方式。该方法的反编译代码如下所示。

[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 是一种反射类型,这里有一个关于它的堆栈溢出问题: What's the difference between System.Type and System.RuntimeType in C#?

【讨论】:

感谢您提供更多信息 - 非常有趣。为此 +1。

以上是关于内部类和公共构造函数 - 为啥它与 Activator.CreateInstance 一起工作?的主要内容,如果未能解决你的问题,请参考以下文章

内部类的公共构造函数是啥意思[重复]

C++类和对象(构造函数static成员内部类友元函数等)

此类未定义公共默认构造函数,或构造函数引发异常。内部异常:java.lang.InstantiationException

构造函数和析构函数的链表问题

如何使用来自内部协议的初始化程序在公共函数中构造新值?

C++类和对象:构造函数初始化友元匿名对象内部类