在.NET 3.5及更低版本中,以下(稍微粗糙的示例)运行得很好:
组件A:
public static class ClassInAssemblyA
{
public static string GetCallingAssemblyLocation()
{
return System.Reflection.Assembly.GetCallingAssembly().GetName(false).CodeBase;
}
}
组件B:
public class ClassInAssemblyB
{
public string AssemblyName { get; private set; }
public ClassInAssemblyB()
{
AssemblyName = ClassInAssemblyA.GetCallingAssemblyLocation();
}
}
组件C:
var assemblyName = new ClassInAssemblyB().AssemblyName;
Assert.That(assemblyName.Contains("AssemblyB"));
不幸的是,.NET 4.0 CLR似乎经过了优化,可以将AssemblyA代码内联到AssemblyB中,因此当在调试模式下执行时,上述测试实际上通过了,但在发布模式下失败。基本上不可能在执行过程中重现错误。
停止内联的一种方法是要求调用程序在每次引用AssemblyA时添加属性[MethodImpl(MethodImplOptions.NoInlining)]
。这是一个笨拙的解决方案,需要调用方了解库的内部工作,这不应该是他们的问题,所以我不愿意走这条路。
是否有其他方法可以在运行时确定调用程序集的文件名?
我认为除了使用MethodImplOptions.NoInlining
之外没有其他解决方案。请参阅GetCallingAssembly的文档,其中几乎包括了这个确切的场景。此外,请注意,您需要将属性添加到中A中的方法和B中的方法,因为内联到其调用程序集中会导致您看到的行为。
我的理解是,JIT编译器将始终尊重[Flags(MethodImplOptions.NoInlining)]
,除非在您尝试测试自递归调用的情况下,可能。
这是基于
- MethodImplOptions的文档(它似乎将尾部递归作为一种特殊情况调用)
- 此处讨论的CLI规范的引用:
http://bytes.com/topic/c-sharp/answers/509557-race-conditions-c-eventing
(据我所知,它没有讨论尾部递归)