静态构造函数未被调用

时间:2016-06-08 08:39:45

标签: c# .net automapper

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

我的理解是Mapper.Map会尝试通过反射来访问类型,它的成员以及第一次使用静态构造函数时会调用它。这是基本的,但挑战我的理解。提供了代码段。

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

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

2 个答案:

答案 0 :(得分:4)

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

Mapper.Map只能访问自己的内部映射“字典” - 在它可能达到处理SampleViewModel之前,它会失败。静态构造函数永远不会运行,因此无法将“自身”添加到Mapper

现在,如果这不涉及反射,那么静态构造函数将被调用是正确的 - 只是因为它会在编译包含访问权限的方法时发生,例如:

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

在这种情况下,由于该方法引用了SampleViewModel上的字段,因此在包含方法的JIT编译期间将调用SampleViewModel的静态构造函数,因此{{1}行将正确执行,因为现在存在映射。不用说这不是解决问题的正确方法。它只会让代码变得非常可怕:)

免责声明:即使这可能现在解决了问题,但它依赖于Windows当前MS.NET实现中的非合同行为。合同指定在对类型成员的任何访问之前调用类型初始值设定项,但这仍然意味着CIL的有效实现可能仅在 Mapper.Map<SampleViewModel>(s)之后调用类型初始值设定项,如只要它发生在Mapper.Map之前 - 即便如此,如果编译器可以确保它是安全的,那么obj.SomeField可能会被优化掉。 强制类型初始化程序调用的唯一真正方法是调用obj.SomeField,但到那时,您也可以添加静态RuntimeHelpers.RunClassConstructor方法或其他东西。

真正的问题是你不应该首先在静态构造函数中初始化这样的东西。应该在某种确定性初始化过程中设置映射,例如,显式调用的Init方法。否则你就会向一大群Heisenbugs敞开心扉,更不用说CLR中的细微变化会破坏你的整个应用程序。

静态构造函数不仅仅用于“注册”,只是类型本身的初始化 - 其他任何东西都是滥用,并且会导致您(或.NET兼容性团队)的麻烦。

答案 1 :(得分:3)

静态构造函数在创建该类的第一个实例之前或在访问该类型的任何静态成员之前的实现定义时间运行。请参阅When is a static constructor called in C#?

映射器尝试在执行其实例化要映射的类的工作之前找到映射,因此无法找到映射,因为该类在此之前从未实例化过。

只需将映射初始化代码移动到AutoMapperBootstrap.cs文件中,然后在应用程序初始化中调用它。