静态构造函数未调用 [英] static constructor not invoked

查看:94
本文介绍了静态构造函数未调用的处理方法,对大家解决问题具有一定的参考价值,需要的朋友们下面随着小编来一起学习吧!

问题描述

我正在尝试使用AutoMapper进行模型-视图模型映射,并希望在类型的静态构造函数中执行一次映射配置.使用该类型调用Mapper.Map(AutoMapper)时,不会调用该类型的静态构造函数.

I'm trying to use the AutoMapper for model-viewmodel mapping and wanted to have the mapping configuration executed once in the static constructor of the type. The static constructor of a type is not invoked when Mapper.Map (AutoMapper) is invoked with that type.

我的理解是,Mapper.Map将尝试通过反射来访问类型,其成员,并且在首次尝试使用时将调用静态构造函数.这是基本的东西,但挑战了我的理解.提供了代码段.

My understanding is that the Mapper.Map would try to access the type, its members through reflection and on the first attempt of the usage the static constructor would be called. This is something basic but challenges my understanding. The code snippet is provided.

class SampleViewModel 
{
    static SampleViewModel()
    {
        Mapper.Initialize(cfg => cfg.CreateMap<Sample, SampleViewModel>().ReverseMap());
    }

    public SampleViewModel()
    {
    }


    public int PropertyA { get; set; }
    public int PropertyB { get; set; }
}


 Sample s = new Sample { PropertyA = 10, PropertyB = 20 };
 var obj = Mapper.Map<SampleViewModel>(s); // fails

第一次通过反射访问类型和成员时,不是调用了静态构造函数(如果提供)吗?

Isn't the static constructor called (if provided) when the type and members are accessed through reflection for the first time?

推荐答案

您没有访问SampleViewModel的任何成员-仅引用类型本身是不够的.

You're not accessing any members of SampleViewModel - it's not enough to just reference the type itself.

Mapper.Map仅访问其自身的内部字典"-在到达SampleViewModel处理点之前,它会失败.静态构造函数永远不会运行,因此无法在Mapper中添加自身".

Mapper.Map only accesses its own internal "dictionary" of mappings - before it could ever get to a point where it deals with a SampleViewModel, it fails. The static constructor never runs, so it cannot add "itself" into the Mapper.

现在,如果这不涉及反射,那么您将对静态构造函数进行调用是正确的-仅因为它会在包含访问的方法的编译期间发生,例如:

Now, if this didn't involve reflection, you would be right that the static constructor would be called - simply because it would happen during the compilation of the method containing the access, e.g.:

var obj = Mapper.Map<SampleViewModel>(s);
Console.WriteLine(obj.SomeField);

在这种情况下,由于该方法正在引用SampleViewModel上的字段,因此SampleViewModel的静态构造函数将在包含方法的JIT编译期间被调用,因此,Mapper.Map<SampleViewModel>(s)行将正确执行,因为现在存在映射.不用说这不是解决您问题的正确方法.这只会使代码的维护变得非常可怕:)

In this case, since the method is referencing a field on SampleViewModel, the static constructor for SampleViewModel will be invoked during JIT compilation of the containing method, and as such, the Mapper.Map<SampleViewModel>(s) line will execute correctly, since the mapping is now present. Needless to say this is not the proper solution to your problem. It would just make the code absolutely horrible to maintain :)

免责声明::即使这可能会立即解决问题,但它取决于Windows上MS.NET的当前实现中的非合同行为.合同规定在对类型成员的任何访问之前都将调用类型初始值设定项,但这仍然意味着有效的CIL实现只能在之后 Mapper.Map之后调用类型初始值设定项.它发生在obj.SomeField之前-甚至在那时,如果编译器可以确保obj.SomeField这样做是安全的,那么它可能已经被优化.强制类型初始化程序的唯一真正方法是调用RuntimeHelpers.RunClassConstructor,但是到那时,您也可以添加静态Init方法或其他方法.

DISCLAIMER: Even though this might fix the problem right now, it depends on a non-contractual behaviour in the current implementation of MS.NET on Windows. The contract specifies that the type initializer is invoked before any access to a member of the type, but that still means that a valid implementation of CIL might only call the type initializer after Mapper.Map, as long as it happens before obj.SomeField - and even then, it might be that obj.SomeField gets optimized away if the compiler can ensure it is safe to do so. The only real way to enforce the call of the type initializer is to call RuntimeHelpers.RunClassConstructor, but by that point, you could just as well add a static Init method or something.

真正的问题是,您首先不应该真正在静态构造函数中初始化像这样的东西.应该在某种确定性的初始化过程(例如,显式调用的InitMappings方法)中设置映射.否则,您将向大量的Heisenbug敞开大门,更不用说CLR的细微变化,无缘无故破坏了整个应用程序.

The real problem is that you shouldn't really initialize stuff like this in a static constructor in the first place. Mappings should be set in some kind of deterministic initialization process, say, an explicitly invoked InitMappings method. Otherwise you're opening yourself to a huge can of Heisenbugs, not to mention subtle changes in the CLR breaking your whole application for no apparent reason.

静态构造函数并不用于注册",而仅用于类型本身的初始化-其他任何事情都是滥用,并且会给您(或.NET兼容性团队)带来麻烦.

Static constructors just aren't meant for "registration", just the initialization of the type itself - anything else is an abuse, and will cause you (or the .NET compatibility team) trouble.

这篇关于静态构造函数未调用的文章就介绍到这了,希望我们推荐的答案对大家有所帮助,也希望大家多多支持IT屋!

查看全文
登录 关闭
扫码关注1秒登录
发送“验证码”获取 | 15天全站免登陆