C# 类的构造函数顺序:参数化、默认和静态?
假设我有一个包含 3 个构造函数、一个默认(无参数)构造函数、一个参数化构造函数和一个静态构造函数的类。 像这样:
public MyClass() { ... }
public MyClass(string arg) : this() { ... }
static MyClass() { ... }
假设我调用参数化构造函数,这些构造函数以什么顺序执行?
我以为它是静态的,然后参数化,然后默认。 但是……我的经验并不同意这一点。
背景:我有一个应用程序,它将引用的 DLL 作为资源嵌入。 注册一个程序集解析器,
static MyClass()
{
AppDomain.CurrentDomain.AssemblyResolve += new ResolveEventHandler(Resolver);
}
在运行时,应用程序通过Resolver 方法的定义
static System.Reflection.Assembly Resolver(object sender, ResolveEventArgs args)
{
....
}
如下所示:我知道解析器可以以它选择的任何方式生成程序集。 就我的应用程序而言,它执行的操作是
Assembly.GetExecutingAssembly().GetManifestResourceStream(name);
其中 name 是嵌入资源的名称。 然后读取该资源的所有字节,并对读取的字节块执行 Assembly.Load(byte[]) 操作。
乍一看,这对您来说可能很奇怪,但它确实有效。
您可能会说,为什么要嵌入程序集,而不仅仅是 ILMerge? 好问题。 我认为我需要嵌入,因为嵌入的程序集已签名,并且我没有重新签名合并的程序集的密钥。 所以我嵌入。
问题是这样的:假设我在类上声明了一个私有实例成员变量,该变量的类型是在嵌入式程序集中定义的。 就我而言,它是一个枚举,我还初始化了该枚举的值。
现在,如果静态构造函数已经运行,那么该私有成员上的初始化程序将不会出现运行问题。 但我看到的是“找不到文件”错误 - 您的基本 Fusion 错误。
Unhandled Exception: System.IO.FileNotFoundException: Could not load file or assembly 'MyApp, Version=1.1.4.1, Culture=neutral, PublicKeyToken=edbe51ad942a3f5c' or one of its dependencies. The system cannot find the file specified.
File name: 'MyApp, Version=1.1.4.1, Culture=neutral, PublicKeyToken=edbe51ad942a3f5c'
WRN: Assembly binding logging is turned OFF.
To enable assembly bind failure logging, set the registry value[HKLM\Software\Microsoft\Fusion!EnableLog] (DWORD) to 1.
Note: There is some performance penalty associated with assembly bind failure logging.
To turn this feature off, remove the registry value [HKLM\Software\Microsoft\Fusion!EnableLog].
如果我删除私有实例变量,那么我不会收到 Fusion 错误。
我可以使用该类型的变量,或者稍后在嵌入式程序集中定义的任何其他类型,只要它们没有作为类中的成员实例变量进行初始化。 我可以在实例方法中使用类型,没问题。
写下这篇文章,我想我可能已经找到了自己问题的答案。 也许这是一个 JIT 计时问题:实例构造函数可能在静态构造函数运行之前被 JIT 化。 这也许会吗? 导致融合错误?
有人有任何见解吗?
这不是一个巨大的关键问题,因为我可以重新设计类来避免这个问题,删除依赖于嵌入式程序集的所有实例变量。 但我想理解它。
Suppose I have a class with 3 constructors, a default (no argument) constructor, a parameterized constructor, and a static constructor. like this:
public MyClass() { ... }
public MyClass(string arg) : this() { ... }
static MyClass() { ... }
Supposing I invoke the parameterized constructor, in what order do these constructors execute?
I thought it was static, then parameterized, then default. But ... my experience isn't agreeing with that.
The background: I have an app that embeds a referenced DLL as a resource. At runtime, the application registers an assembly resolver via
static MyClass()
{
AppDomain.CurrentDomain.AssemblyResolve += new ResolveEventHandler(Resolver);
}
where the Resolver method is defined like this:
static System.Reflection.Assembly Resolver(object sender, ResolveEventArgs args)
{
....
}
I understood that the Resolver can produce an assembly any way it darn well chooses. In my app's case, it does a
Assembly.GetExecutingAssembly().GetManifestResourceStream(name);
where name is the name of the embedded resource. Then read all the bytes of this resource, and do an Assembly.Load(byte[]) on the block of bytes that is read.
This may sound strange to you, at first, but it works.
You might say, why in the world would you embed an assembly, rather than just ILMerge? Good question. I think I need to embed because the embedded assembly is signed, and I don't have the key to re-sign the merged assembly. So I embed.
The problem is this: suppose I declare a private instance member variable on the class, which is of a type that is defined in the embedded assembly. In my case, it's an enum, and I also initialize the value of that enum.
Now, if the static constructor has already run, then the initializer on that private member will have no problem running. But what I see is a "file not found" error - your basic Fusion error.
Unhandled Exception: System.IO.FileNotFoundException: Could not load file or assembly 'MyApp, Version=1.1.4.1, Culture=neutral, PublicKeyToken=edbe51ad942a3f5c' or one of its dependencies. The system cannot find the file specified.
File name: 'MyApp, Version=1.1.4.1, Culture=neutral, PublicKeyToken=edbe51ad942a3f5c'
WRN: Assembly binding logging is turned OFF.
To enable assembly bind failure logging, set the registry value[HKLM\Software\Microsoft\Fusion!EnableLog] (DWORD) to 1.
Note: There is some performance penalty associated with assembly bind failure logging.
To turn this feature off, remove the registry value [HKLM\Software\Microsoft\Fusion!EnableLog].
If I remove the private instance variable, then I don't get the Fusion error.
I can use variables of that type, or any other type defined in the embedded assembly, later, as long as they are not initialized on as member instance variables in the class. I can use the types in a instance method, no problem.
Writing this up, I am thinking I may have come up with the answer to my own question. Maybe it is a JIT timing issue: maybe the instance constructors are being JIT'd before the static constructor is run. This would, maybe? lead to the Fusion error?
anyone have any insight?
It's not a huge crucial problem, because I can redesign the class to avoid the issue, removing all instance variables that depend on the embedded assembly. But I'd like to understand it.
如果你对这篇内容有疑问,欢迎到本站社区发帖提问 参与讨论,获取更多帮助,或者扫码二维码加入 Web 技术交流群。
绑定邮箱获取回复消息
由于您还没有绑定你的真实邮箱,如果其他用户或者作者回复了您的评论,将不能在第一时间通知您!
发布评论
评论(1)
就顺序而言,你是对的。
首先运行静态构造函数,然后运行非参数化构造函数,最后运行参数化构造函数。
JIT 计时不应成为问题。 CLR 保证静态构造函数将在构造任何实例之前完成。
但是,程序集解析是在静态构造函数触发之前发生的。 运行时需要在调用任何静态构造之前解析程序集(及其依赖项)。 这就是你遇到这个问题的原因。
You are right, in terms of the order.
The static constructor runs first, then the non-parameterized constructor, then the parameterized constructor.
The JIT timing should not be an issue. The CLR guarantees that your static constructor will complete prior to any instance being constructed.
However, the assembly resolution is happening BEFORE your static constructor fires. The runtime needs to resolve the assembly (and its dependencies) prior to invoking any static construction. That's why you're running into this problem.