.Net Lazy Initialization свойства Singleton - PullRequest
2 голосов
/ 15 февраля 2012

У меня есть класс, DataAdapter, который создается моим сайтом как одноэлементный объект. Этот класс имеет свойство Person, которое я хотел бы использовать как ленивый синглтон, код для этого:

private readonly object _personLock = new object();
private volatile IPersonManager _person;
public IPersonManager Person
{
    get
    {
        if (_person == null)
        {
            lock (_personLock)
            {
                if (_person == null)
                {
                    _person = new PersonManager(_adUserName, _adPassword, client);
                }
            }
        }
        return _person;
    }
}

(эти три аргумента конструктора PersonManager являются свойствами / полями текущего объекта.) Этот код отлично работает (это шаблон проверка двойного замка ).

Однако, это много кода, я хотел бы использовать новый Lazy <> тип в .Net 4.0, чтобы упростить его. Поэтому я изменил код на:

    private static readonly Lazy<IPersonManager> _person = new Lazy<IPersonManager>(() => new PersonManager(_adUserName, _adPassword, client));
    public static IPersonManager Person { get { return _person.Value; } }

Но это не работает, потому что эти три параметра не являются статическими (они являются объектами экземпляра в текущем методе). Ни один из записать ups Я не нашел адреса этого. Мне нужен какой-то способ передать эти значения в это лямбда-выражение? Класс Lazy <> выглядит так, как будто ожидает пустой подписи.

Ответы [ 2 ]

3 голосов
/ 15 февраля 2012

Ну, а что, если Lazy использует свойство Instance для работы с экземпляром вашего синглтона, чтобы обеспечить себя свойствами?Эти поля все еще могут быть закрытыми, так как мы работаем с ними внутри класса (умные), и все это будет оставаться ленивым, пока на Singleton.Instance не будет ссылка впервые во время выполнения.Однако частные поля ДОЛЖНЫ иметь правильные значения, прежде чем ваш код попытается получить свойство Person.Если они активно загружаются при создании экземпляра Singleton, это нормально.

Заимствование из C # In Depth , вот квази-ленивый Singleton с полностью ленивым членом Person, инициализированным с помощью лямбды, котораяссылается на Singleton.

public sealed class Singleton
{
    private static readonly Singleton instance = new Singleton();

    // Explicit static constructor to tell C# compiler
    // not to mark type as beforefieldinit
    static Singleton()
    {
    }

    private Singleton()
    {
       //I HIGHLY recommend you initialize _adusername, 
       //_adpassword and client here.
    }

    public static Singleton Instance
    {
        get
        {
            return instance;
        }
    }

    private static readonly Lazy<IPersonManager> _person = 
       new Lazy<IPersonManager>(() => new PersonManager(Instance._adUserName, Instance._adPassword, Instance.client));
    public static IPersonManager Person { get { return _person.Value; } }

    private object _adUserName;
    private object _adPassword;
    private object client;
}

public class PersonManager:IPersonManager {}

public interface IPersonManager{}

РЕДАКТИРОВАТЬ: Если у вас есть IoC, используйте IoC.В настоящее время вы пытаетесь смешивать шаблоны;вы используете IoC для "преобразования" класса экземпляра в синглтон с использованием правил времени выполнения, но затем пытаетесь создать экземпляр ленивого статического свойства на основе компилятора на основе полей данных этого поддельного экземпляра в области видимости.Это просто не сработает

Как только вы перейдете на IoC, КАЖДУЮ зависимость нужно зарегистрировать и ввести.Зарегистрируйте PersonManager в Ninject в качестве реализации IPersonManager, а затем создайте конструктор для основного одноэлементного адаптера данных, которому может быть предоставлен Func, который создает IPersonManager.Обычно для этой цели можно определить пользовательскую функцию, которая в вашем случае будет использовать IoC для предоставления необходимых данных экземпляра из одного экземпляра DataAdapter, хранящегося в контейнере.

Предостережение: теперь эти поля данных должны быть доступны для публичного чтения, чтобы избежать какого-либо серьезного отвратительного отражения;Вы можете определить поля как поля только для чтения или свойства «только для чтения», чтобы предотвратить вмешательство в них людей, но ваши потребители смогут их видеть.

РЕДАКТИРОВАТЬ 2: Вот что яимел в виду:

//in your Ninject bindings:
kernel.Bind<DataAdapter>().ToSelf().InSingletonScope();
kernel.Bind<PersonManager>().ToSelf().InSingletonScope();
//to bind the interface
kernel.Bind<IPersonManager>()
   .ToMethod(c =>{ 
      var adapter = kernel.Get<DataAdapter>();
      //this is why these fields would have to be public
      var arg1 = new ConstructorArgument("adUserName", adapter._adUserName)
      var arg2 = new ConstructorArgument("adPassword", adapter._adPassword)
      var arg3 = new ConstructorArgument("client", adapter.client)
      //the names of the arguments must match PersonManager's constructor
      c.Kernel.Get<PersonManager>(arg1, arg2, arg3);
   });

//now in your DataAdapter, specify a constructor like this, and Ninject will provide:

public DataAdapter(Func<IPersonManager> personFunc)
{
   //_person should obviously not be instantiated where it's defined in this case
   _person = new Lazy<IPersonManager>(personFunc);
}
0 голосов
/ 15 февраля 2012

(эти три аргумента конструктора PersonManager свойства / поля текущего объекта.)

Конструктор используется для инициализации вашего объекта. Аргументы, которые вы пытаетесь передать, не имеют значения, назначенного им на данный момент. Если вашему объекту нужно, чтобы эти значения были правильно инициализированы, то их нужно передать при инициализации.

Хотя вы можете преобразовать свое свойство в метод и передать эти значения статическому методу GetInstance, вы установите его только один раз, при первом вызове GetInstance. Это, вероятно, не очень хорошая идея, но это может быть сделано. Я бы преобразовал ваше свойство Person в метод и принял бы эти параметры и использовал бы их для инициализации конструктора. Это означает, что вы не будете использовать Lazy<T>, и ваши строки кода увеличатся, но у вас будет гораздо более предсказуемое поведение.

...