Logo Questions Linux Laravel Mysql Ubuntu Git Menu
 

Does the location of a lambda expression matter in Lazy Initialization?

Tags:

c#

.net

I tried the Lazy Initialization sample code from MSDN. And I tried to mimic the static InitLargeObject() method with a static Func< T > delegate using a lambda expression since it's acceptable by the Lazy< T > constructor.

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();
}

If I put the InitWithLambda delegate before the lazyLargeObject declaration, everything is fine.

If I put the InitWithLambda delegate after the lazyLargeObject declaration, I got this error:

Unhandled Exception: System.TypeInitializationException: The type initializer for 'Program' threw an exception. ---> System.ArgumentNullException: Value cannot be null. Parameter name: valueFactory at System.Lazy1..ctor(Func1 valueFactory, LazyThreadSafetyMode mode) at System.Lazy1..ctor(Func1 valueFactory) at Program..cctor() in E:\myCode\Misc\LazyWithLambda\LazyWithLambda\Class1.cs:line 10 --- End of inner exception stack trace --- at Program.Main()

It seems the lambda expression failed to be assinged to the valueFactory parameter.

But it seems the location doesn't affect the InitLargeObject() method, which is not using Lambda expression.

Why?

Update 1

According to Billy ONeal, I reproed this issue with a simpler code:

This one is ok:

class FieldInitInOrder
{        
    static string s1 = "abc";
    static Int32 s1_length = s1.Length;

    static void Main()
    {
        Console.WriteLine(s1_length);
    }
}

This one throw the same NullReference exception:

class FieldInitInOrder
{
    static Int32 s1_length = s1.Length;  // Order switched
    static string s1 = "abc";  // Order switched

    static void Main()
    {
        Console.WriteLine(s1_length);
    }
}

I don't know why C# compiler is designed like this. It could cause very delicate bugs. Is there any design consideration?

like image 216
smwikipedia Avatar asked Jul 05 '14 14:07

smwikipedia


1 Answers

C# initializes members in the order in which they are declared.

like image 193
Billy ONeal Avatar answered Oct 04 '22 13:10

Billy ONeal