内部类和公共构造函数 - 为啥它与 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 一起工作?的主要内容,如果未能解决你的问题,请参考以下文章
此类未定义公共默认构造函数,或构造函数引发异常。内部异常:java.lang.InstantiationException