【发布时间】:2014-07-05 14:43:47
【问题描述】:
我尝试了来自 MSDN 的延迟初始化 sample code。并且我尝试使用 lambda 表达式模拟具有静态 Func 委托的静态 InitLargeObject() 方法,因为 Lazy 构造函数可以接受它。
static Lazy<LargeObject> lazyLargeObject = new Lazy<LargeObject>(InitWithLambda);
static Func<LargeObject> InitWithLambda = () =>
{
LargeObject large = new LargeObject(Thread.CurrentThread.ManagedThreadId);
// Perform additional initialization here.
return large;
};
static LargeObject InitLargeObject()
{
LargeObject large = new LargeObject(Thread.CurrentThread.ManagedThreadId);
// Perform additional initialization here.
return large;
}
static void Main()
{
//lazyLargeObject = new Lazy<LargeObject>(InitLargeObject); // <---- This one use the static method.
lazyLargeObject = new Lazy<LargeObject>(InitWithLambda); // <---- Thsi one uses the lambda expression.
Console.WriteLine(
"\r\nLargeObject is not created until you access the Value property of the lazy" +
"\r\ninitializer. Press Enter to create LargeObject.");
Console.ReadLine();
// Create and start 3 threads, each of which uses LargeObject.
Thread[] threads = new Thread[3];
for (int i = 0; i < 3; i++)
{
threads[i] = new Thread(ThreadProc);
threads[i].Start();
}
// Wait for all 3 threads to finish.
foreach (Thread t in threads)
{
t.Join();
}
Console.WriteLine("\r\nPress Enter to end the program");
Console.ReadLine();
}
如果我将 InitWithLambda 委托放在 lazyLargeObject 声明之前,一切都很好。
如果我将 InitWithLambda 委托 放在 lazyLargeObject 声明之后,我会收到以下错误:
未处理的异常:System.TypeInitializationException:类型 “程序”的初始化程序引发了异常。 ---> System.ArgumentNullException:值不能为空。参数名称: System.Lazy
1..ctor(Func1 valueFactory 处的 valueFactory, LazyThreadSafetyMode 模式)在 System.Lazy1..ctor(Func1 valueFactory) 在 Program..cctor() 中 E:\myCode\Misc\LazyWithLambda\LazyWithLambda\Class1.cs:第 10 行 --- 内部异常堆栈跟踪结束 --- 在 Program.Main()
似乎无法将 lambda 表达式分配给 valueFactory 参数。
但似乎该位置不影响 InitLargeObject() 方法,它没有使用 Lambda 表达式。
为什么?
更新 1
根据Billy ONeal,我用更简单的代码重现了这个问题:
这个还可以:
class FieldInitInOrder
{
static string s1 = "abc";
static Int32 s1_length = s1.Length;
static void Main()
{
Console.WriteLine(s1_length);
}
}
这个抛出同样的 NullReference 异常:
class FieldInitInOrder
{
static Int32 s1_length = s1.Length; // Order switched
static string s1 = "abc"; // Order switched
static void Main()
{
Console.WriteLine(s1_length);
}
}
我不知道为什么 C# 编译器是这样设计的。它可能会导致非常微妙的错误。 有没有设计考虑?
【问题讨论】:
-
通常不可能使编译器确保安全排序。你可以说
static bool dummy = DoAnything(ref s1_length, ref s1);。此处编译器无法保证安全。 -
关于您的后续问题:字段初始化器完全依赖于初始化顺序根本不是一个好主意。如果您需要遵循逻辑顺序,请使用构造函数。编译器不可能在每种情况下都猜出正确的顺序。正如您所提到的,如果另一个程序员决定进行一些重构,或者通常不会产生任何影响的事情,这很容易导致错误。