【问题标题】:Does the location of a lambda expression matter in Lazy Initialization?lambda 表达式的位置在延迟初始化中是否重要?
【发布时间】: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.Lazy1..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);。此处编译器无法保证安全。
  • 关于您的后续问题:字段初始化器完全依赖于初始化顺序根本不是一个好主意。如果您需要遵循逻辑顺序,请使用构造函数。编译器不可能在每种情况下都猜出正确的顺序。正如您所提到的,如果另一个程序员决定进行一些重构,或者通常不会产生任何影响的事情,这很容易导致错误。

标签: c# .net


【解决方案1】:

C# 按照声明的顺序初始化成员。

【讨论】:

  • 赞成。这里的“成员”只是具有字段初始值设定项的 static 字段。静态字段初始值设定项按其 textual 顺序运行。如果类/结构是partial 并且在类的几个“部分”中有静态字段使得这些字段相互引用,我认为顺序是未指定的。在复杂情况下,删除字段初始值设定项并在静态构造函数中按所需顺序编写赋值总是一个好主意。
  • 非静态字段初始化器也以文本顺序运行,但这仅在实例字段初始化器具有“静态”副作用时才相关。例如字段不允许相互引用。
【解决方案2】:

这是设计使然,根据 C# 规范:

名称声明的文本顺序通常不 意义。特别是,文本顺序对于 命名空间、常量、方法、属性的声明和使用, 事件、索引器、运算符、实例构造函数、析构函数、 静态构造函数和类型。声明顺序在 有以下几种方式:

• 字段声明和局部变量的声明顺序 声明确定其初始化程序(如果有)的顺序 被执行。

• 局部变量必须在使用前定义 (§3.7)。

• 枚举成员声明(第 14.3 节)的声明顺序是 当常量表达式值被省略时显着。

这些值将按照它们声明的顺序进行初始化。

【讨论】:

    猜你喜欢
    • 1970-01-01
    • 1970-01-01
    • 1970-01-01
    • 1970-01-01
    • 2020-05-10
    • 1970-01-01
    • 2011-11-17
    • 1970-01-01
    • 1970-01-01
    相关资源
    最近更新 更多