几周前,我从 Java 转向 C#。今天,我有一个奇怪的行为,我尝试在这个简单的示例中重现它。我正在使用 .net FW 4。
我有三门课: 首先,抽象的:
namespace ReadonlyStaticOrder
{
using System;
using System.Collections.Generic;
public abstract class AbstractClass
{
public AbstractClass(string value, IEnumerable<string> someValues)
{
if (value == null)
{
throw new ArgumentNullException("value");
}
if (someValues == null)
{
throw new ArgumentNullException("someValues");
}
// would do something after...
}
}
}
第二个:
namespace ReadonlyStaticOrder
{
using System.Collections.Generic;
public sealed class ReadonlyOrderInitialization : AbstractClass
{
// this line introduces the bug, since it call the ctor before SomeValues already initialized
// if removed, no more exception
public static readonly ReadonlyOrderInitialization Sample = new ReadonlyOrderInitialization("sample");
private static readonly IEnumerable<string> SomeValues = new string[] { "one", "two", "three" };
public ReadonlyOrderInitialization(string value)
: base(value, SomeValues)
{
}
}
}
示威者:
namespace ReadonlyStaticOrder
{
using System;
public sealed class Program
{
static void Main(string[] args)
{
try
{
new ReadonlyOrderInitialization("test");
}
catch (TypeInitializationException typeInitializationException)
{
Console.WriteLine(typeInitializationException.Message);
Console.WriteLine(typeInitializationException.InnerException.Message);
Console.WriteLine(typeInitializationException.StackTrace);
}
Console.ReadLine();
}
}
}
输出为:
类型初始值设定项 “ReadonlyStaticOrder.ReadonlyOrderInitialization”引发异常。 值不能为空。参数名称:someValues at ReadonlyStaticOrder.ReadonlyOrderInitialization..ctor(字符串值)
在 ReadonlyStaticOrder.Program.Main(String[] args) 中 d:\stackoverflow\静态 readonlyissue\ConsoleApplication1\ReadonlyStaticOrder\Program.cs:行 12
我在引入该错误的行上添加了注释。对我来说,编译器必须警告我,由于静态初始化的顺序,行为可能会很奇怪。难道我错了?
谢谢你们,我希望你们有足够的信息。
它定义为文本顺序 - ECMA 334 中的第 17.11 节:
如果类包含任何带有初始值设定项的静态字段,则这些初始值设定项将在执行静态构造函数之前立即按文本顺序执行。
顺便说一句,如果您考虑partial classes
,那么这会变得
特别有趣,在这种情况下:它没有定义。
如果有疑问,请将初始化显式移至静态构造函数。
至于为什么;考虑(注:这些只是我自己的想法):
partial classes
问题,完整的订单本身没有严格定义;所以它无法处理一般情况 - 再说一次,覆盖特定情况(单个类片段等)又回到了“薄饰面”(它只警告明显的情况,但无法帮助解决非平凡的)通常,如果您在初始化之前尝试使用成员,编译器会警告您。
在这种情况下,您可以绕过此检查,因为静态成员不直接使用另一个静态成员,而是调用使用另一个静态成员的构造函数。
编译器无法保护您免受所有可能的依赖问题的影响,只能保护简单的问题。这只是一个过于复杂的步骤,编译器无法捕捉到。
当然,编译器有可能捕获这样的东西,但这会使每个额外的依赖级别变得更加复杂,并且仍然不可能捕获所有情况。