Параллельные расширения: помогите понять LazyInit‹T›

Обновление для будущих читателей. Когда выйдет .NET 4, LazyInit<T> из CTP будет переименовано в Lazy<T> и преобразовано из структуры в класс, поэтому мало что из этого применимо, за исключением иллюстрация того, почему изменяемые структуры могут быть проблематичными, если вы не будете осторожны.

Я экспериментировал с LazyInit в июньской CTP-программе параллельных расширений, и я ожидаю, что следующий код будет выводить один и тот же Guid тысячу раз, но вместо этого он выводит тысячу разных Guid. Ясно, что мне не хватает чего-то очевидного здесь о том, как LazyInit должен работать, и я был бы признателен, если бы кто-нибудь любезно указал, что это такое.

using System;
using System.Diagnostics;
using System.Threading;

namespace TestApp
{
    class Program
    {
        static void Main(string[] args)
        {
            for (int i=0; i < 1000; i++)
            {
                Console.WriteLine(TestClass.Instance.Id);
            }

            Console.Write("Press any key to continue:");
            Console.ReadKey();
        }

        private class TestClass
        {
            private static readonly LazyInit<TestClass> _instance = new LazyInit<TestClass>(() => new TestClass(), LazyInitMode.EnsureSingleExecution);

            public static TestClass Instance
            {
                get { return _instance.Value; }
            }

            private TestClass()
            {
                Debug.WriteLine("TestClass Constructor");
                Id = Guid.NewGuid();
            }

            public Guid Id { get; private set; }
        }
    }
}

person Joel Mueller    schedule 11.11.2008    source источник


Ответы (1)


Краткая версия: сделайте static не только для чтения, и это исправит ошибку, с которой вы столкнулись.

Длинная версия: это очень неправильно понятая часть C#. Когда вы обращаетесь к структуре, вы получаете доступ к копии структуры. Базовый вызов LazyInit.Value является изменяющей операцией. Обычно выполняется обратное копирование, но в случае поля, доступного только для чтения, невозможно выполнить обратное копирование, и, следовательно, вы все еще остаетесь с неинициализированным значением.

Чрезвычайно подробное объяснение: http://ericlippert.com/2008/05/14/mutating-readonly-structs/

person JaredPar    schedule 11.11.2008
comment
Спасибо, я вернулся, чтобы ответить на свой вопрос с тем же URL-адресом, но вы меня опередили! - person Joel Mueller; 11.11.2008