Мой подход к ленивой загрузке некорректен? - PullRequest
0 голосов
/ 24 апреля 2009

Платформа : Visual Studio 2008 SP1 с Resharper 4.1, .NET 3.5

У меня есть класс со статическим методом, GetProperty<T>, который лениво возвращает значение свойства.

private static T GetProperty<T>(T backingField, Func<T> factory) 
    where T : class 
{
    if (backingField == null)
        backingField = factory();
    return backingField;
}

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

alt text

Это предупреждение игнорируется ?
- Или -
Мой подход к загрузке собственности некорректен?

Ответы [ 4 ]

13 голосов
/ 24 апреля 2009

Ваш метод несовершенен. Чтобы воспользоваться этим подходом, вам нужно задать параметр backingField ref.

private static T GetProperty<T>(ref T backingField, Func<T> factory)

Затем на GetProperty, передать ref _ImagXpress или ref _PdfXpress.

То, как вы это делаете сейчас, просто присваивает новое значение параметру, а не фактическому вспомогательному полю.

5 голосов
/ 24 апреля 2009

Ваш подход несовершенен. Ваши поля никогда не будут установлены на что-либо. аргумент backingField устанавливается в методе GetProperty<T>, но это не обновляет передаваемое поле. Вам нужно передать этот аргумент с прикрепленным к нему ключевым словом ref следующим образом:

private static T GetProperty<T>(ref T backingField, Func<T> factory)
3 голосов
/ 24 апреля 2009

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

Здесь можно вывести шаблон:

public class LazyInit<T>
    where T : class
{
    private readonly Func<T> _creationMethod;
    private readonly object syncRoot;
    private T _instance;

    [DebuggerHidden]
    private LazyInit()
    {
        syncRoot = new object();
    }

    [DebuggerHidden]
    public LazyInit(Func<T> creationMethod)
        : this()
    {
        _creationMethod = creationMethod;
    }

    public T Instance
    {
        [DebuggerHidden]
        get
        {
            lock (syncRoot)
            {
                if (_instance == null)
                    _instance = _creationMethod();
                return _instance;
            }
        }
    }

    public static LazyInit<T> Create<U>() where U : class, T, new()
    {
        return new LazyInit<T>(() => new U());
    }

    [DebuggerHidden]
    public static implicit operator LazyInit<T>(Func<T> function)
    {
        return new LazyInit<T>(function);
    }
}

Это позволяет вам сделать это:

public class Foo
{
    private readonly LazyInit<Bar> _bar1 = LazyInit<Bar>.Create<Bar>();
    private readonly LazyInit<Bar> _bar2 = new LazyInit<Bar>(() => new Bar("foo"));

    public Bar Bar1
    {
        get { return _bar1.Instance; }
    }

    public Bar Bar2
    {
        get { return _bar2.Instance; }
    }
}
1 голос
/ 24 апреля 2009

Чтобы предложить другое решение:

Я бы сказал, что вы не экономите много, инкапсулируя эту логику в отдельный метод. Вы увеличиваете свою сложность без особой выгоды. Я бы предложил сделать это следующим образом:

protected PdfXpress PdfXpress
{
    get
    {
        if (_PdfXpress == null)
            _PdfXpress = PdfXpressSupport.Create();

        return _PdfXpress;
    }
}

protected ImagXpress ImagXpress
{
    get
    {
        if (_ImagXpress == null)
            _ImagXpress = IMagXpressSupport.Create();

        return _ImagXpress;
    }
}

Вы добавляете несколько строк, но значительно уменьшаете сложность.

...