静态构造函数在同一个appdomain中被调用两次?

时间:2012-02-08 11:29:47

标签: c# .net log4net fody-costura

这个问题更多的是关于C#而不是关于log4net(我认为)。

我创建了一个自定义appender,让它读取程序先前设置的静态字段。

令我惊讶的是,静态字段被重新初始化,并且设定值不会使其成为附加信息。

我启动了debugview,发现静态构造函数被调用了两次(!)。这应该在同一个appdomain中不可能吗? 只有debugview才能解决这个问题,因为VS在断点上没有第二次出现。

请注意,这不是关于避免在log4net中使用静态变量的问题。我对log4net使用什么样的魔法来实现这个目标感兴趣?

编辑#1

你好约翰,大粉丝。

我按要求进一步隔离了它。首先,我开始空白并努力解决暴露错误的目标情况。由于我几乎与角色的目标角色匹配,但仍然没有重复,我反过来了。

从错误情况开始,我删除了所有我认为不重要的东西,直到它开始......按预期工作。

当运行时尝试解析log4net程序集时(如在调试模式中观察到的那样),似乎有一些奇怪的事情。

这是我在debugview中看到的:

[7756]一般:WARN - 无法解析模块的'log4net'版本。异常:System.NullReferenceException:未将对象引用设置为对象的实例。 [7756]在DebuggerShared.Services.EventArgs.ModuleLoadedInDebuggerEventArgs..ctor(String modulePath,String moduleLoadMessage,Boolean isUserCode,String name,String version) [7756]一般:WARN - 无法解析模块的“FollowUp.Common”版本。异常:System.NullReferenceException:未将对象引用设置为对象的实例。 [7756]在DebuggerShared.Services.EventArgs.ModuleLoadedInDebuggerEventArgs..ctor(String modulePath,String moduleLoadMessage,Boolean isUserCode,String name,String version)

VS在调试模块屏幕中没有显示路径的值。现在我是如何设法达到这种状况的?奇怪的是,它设法加载了一个程序集,但不知道从哪里:)

这是一个孤立的情况,如果我进一步修改它,它会按预期开始工作。

https://www.sugarsync.com/pf/D6486369_1701716_00940

我仍然对技术细节感兴趣,但在删除了对log4net的引用并再次添加之后,它们又开始重新运行了。 我很高兴它有效,但它让我觉得我没有彻底的解释

此外,静态构造函数*现在被调用两次,这是有意义的,因为当log4net得到它时,类型会再次初始化。

我认为花费更多时间在这个原因上是不值得的,我认为解决方案处于一种奇怪的状态并且理解所有这些都具有边际价值。 不过,如果你能想到解释这个的话,我会很高兴来到这里。

编辑#2

事实证明,某些程序集确实已加载两次,包括具有静态构造函数的程序集。我稍后会调查这是如何实现的,但我通过禁用和启用Costura来解决这个问题。 Costura是一个msbuild任务,它将所有程序集合并为一个。 我不是说Costura是根本原因。可能很容易就是csproj / sln文件处于异常状态。

考虑如何在未来更快地诊断此问题我启动了sysinternals ProcessExplorer。 现在我希望看到程序集只被加载一次,但我发现它们被加载了两次。似乎这是仅在.NET 4中修复的运行时中的错误

http://forum.sysinternals.com/why-some-net-assemblies-are-duplicated-in-memory_topic15279.html https://connect.microsoft.com/VisualStudio/feedback/details/467560/clr-maps-assemblies-into-the-virtual-address-space-twice

编辑#3 Costura使组件加载两次。问题在同一天由项目所有者修复:http://code.google.com/p/costura/issues/detail?id=17&thanks=17&ts=1328826304

我们需要一个Costura标签,但我没有必要的1500点声望点。如果您拥有这些权利,请创建它。感谢。

亲切的问候,汤姆

2 个答案:

答案 0 :(得分:4)

您似乎设法将log4net的两个单独实例加载到同一AppDomain

一个项目参考:

<Reference Include="log4net">
  <HintPath>..\packages\log4net.1.2.11\lib\net35-full\log4net.dll</HintPath>
</Reference>

另一个:

<Reference Include="log4net, Version=1.2.10.0, Culture=neutral, PublicKeyToken=1b44e1d426115821, processorArchitecture=MSIL">
  <SpecificVersion>False</SpecificVersion>
  <HintPath>..\ExternalReferences\log4net.dll</HintPath>
</Reference>

其中一个是强名,另一个不是,这导致.net给他们不同的身份。提示路径也不同。另外一个似乎是1.2.10,另一个1.2.11

尝试调用AppDomain.GetAssemblies()并检查log4net是否出现两次。

答案 1 :(得分:1)

好吧可以显式调用类型初始值设定项:

var initializer = typeof(Foo).TypeInitializer;
initializer.Invoke(null);

但是,我希望希望它没有这样做。你能想出一个简短而完整的程序来证明这种情况吗?