未调用静态构造函数
static constructor not invoked
我正在尝试使用 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
第一次通过反射访问类型和成员时,是否调用静态构造函数(如果提供)?
您没有访问 SampleViewModel
的任何成员 - 仅引用类型本身是不够的。
Mapper.Map
只访问它自己内部的 "dictionary" 映射 - 在它到达处理 SampleViewModel
的点之前,它失败了。静态构造函数从不运行,因此它不能将 "itself" 添加到 Mapper
.
现在,如果这不涉及反射,那么调用静态构造函数是正确的——只是因为它会在包含访问的方法的编译过程中发生,例如:
var obj = Mapper.Map<SampleViewModel>(s);
Console.WriteLine(obj.SomeField);
在这种情况下,由于该方法正在引用 SampleViewModel
上的字段,因此 SampleViewModel
的静态构造函数将在包含方法的 JIT 编译期间被调用,因此,Mapper.Map<SampleViewModel>(s)
行将正确执行,因为映射现在存在。不用说 这不是您问题的正确解决方案。这只会使代码维护起来非常可怕:)
免责声明: 尽管这可能会立即解决问题,但它取决于 Windows MS.NET 当前实施中的非合同行为].契约指定类型初始值设定项在任何访问类型成员之前被调用,但这仍然意味着 CIL 的有效实现可能只调用类型初始值设定项 after Mapper.Map
,只要它发生在 obj.SomeField
之前——即便如此,如果编译器可以确保这样做是安全的,那么 obj.SomeField
也可能会被优化掉。 强制调用类型初始值设定项的唯一真正方法是调用RuntimeHelpers.RunClassConstructor
,但到那时,您也可以添加一个静态Init
方法什么的。
真正的问题是你不应该首先在静态构造函数中真正初始化这样的东西。映射应该在某种确定性的初始化过程中设置,比如说,一个显式调用的 InitMappings
方法。否则,您将面临大量的 Heisenbug,更不用说 CLR 的细微变化无缘无故地破坏了您的整个应用程序。
静态构造函数并不适用于 "registration",仅适用于类型本身的初始化 - 任何其他都是滥用,并且会给您(或 .NET 兼容团队)带来麻烦。
静态构造函数 运行 在实现定义的时间,就在创建 class 的第一个实例之前,或在访问该类型的任何静态成员之前。参见 When is a static constructor called in C#?。
映射器试图在 之前找到一个映射 来实例化要映射到的 class ,因此找不到映射,因为 class在那一刻之前从未被实例化。
只需将您的映射初始化代码移动到 AutoMapperBootstrap.cs
文件或其他文件中,然后在您的应用程序初始化中调用它。
我正在尝试使用 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
第一次通过反射访问类型和成员时,是否调用静态构造函数(如果提供)?
您没有访问 SampleViewModel
的任何成员 - 仅引用类型本身是不够的。
Mapper.Map
只访问它自己内部的 "dictionary" 映射 - 在它到达处理 SampleViewModel
的点之前,它失败了。静态构造函数从不运行,因此它不能将 "itself" 添加到 Mapper
.
现在,如果这不涉及反射,那么调用静态构造函数是正确的——只是因为它会在包含访问的方法的编译过程中发生,例如:
var obj = Mapper.Map<SampleViewModel>(s);
Console.WriteLine(obj.SomeField);
在这种情况下,由于该方法正在引用 SampleViewModel
上的字段,因此 SampleViewModel
的静态构造函数将在包含方法的 JIT 编译期间被调用,因此,Mapper.Map<SampleViewModel>(s)
行将正确执行,因为映射现在存在。不用说 这不是您问题的正确解决方案。这只会使代码维护起来非常可怕:)
免责声明: 尽管这可能会立即解决问题,但它取决于 Windows MS.NET 当前实施中的非合同行为].契约指定类型初始值设定项在任何访问类型成员之前被调用,但这仍然意味着 CIL 的有效实现可能只调用类型初始值设定项 after Mapper.Map
,只要它发生在 obj.SomeField
之前——即便如此,如果编译器可以确保这样做是安全的,那么 obj.SomeField
也可能会被优化掉。 强制调用类型初始值设定项的唯一真正方法是调用RuntimeHelpers.RunClassConstructor
,但到那时,您也可以添加一个静态Init
方法什么的。
真正的问题是你不应该首先在静态构造函数中真正初始化这样的东西。映射应该在某种确定性的初始化过程中设置,比如说,一个显式调用的 InitMappings
方法。否则,您将面临大量的 Heisenbug,更不用说 CLR 的细微变化无缘无故地破坏了您的整个应用程序。
静态构造函数并不适用于 "registration",仅适用于类型本身的初始化 - 任何其他都是滥用,并且会给您(或 .NET 兼容团队)带来麻烦。
静态构造函数 运行 在实现定义的时间,就在创建 class 的第一个实例之前,或在访问该类型的任何静态成员之前。参见 When is a static constructor called in C#?。
映射器试图在 之前找到一个映射 来实例化要映射到的 class ,因此找不到映射,因为 class在那一刻之前从未被实例化。
只需将您的映射初始化代码移动到 AutoMapperBootstrap.cs
文件或其他文件中,然后在您的应用程序初始化中调用它。