Как реализовать отложенную загрузку с помощью PostSharp? - PullRequest
3 голосов
/ 02 марта 2012

Я бы хотел реализовать отложенную загрузку свойств с помощью PostSharp.

Чтобы сделать его коротким, вместо записи

SomeType _field = null;
private SomeType Field
{
    get
    {
        if (_field == null)
        {
            _field = LongOperation();
        }
        return _field;
    }
}

Я хотел бы написать

[LazyLoadAspect]
private object Field
{
    get
    {
        return LongOperation();
    }
}

Итак, я идентифицирую, что мне нужно генерировать некоторый код в классе для генерации вспомогательного поля, а также внутри метода getter для реализации теста.

В PostSharp я рассматривал возможность переопределения CompileTimeInitialize, но мне не хватает знаний, чтобы справиться с скомпилированным кодом.

EDIT: Вопрос можно распространить на любой метод без параметров, например:

SomeType _lazyLoadedField = null;
SomeType LazyLoadableMethod ()
{
    if(_lazyLoadedField ==null)
    {
        // Long operations code...
        _lazyLoadedField = someType;
    }
    return _lazyLoadedField ;
}

станет

[LazyLoad]
SomeType LazyLoadableMethod ()
{
     // Long operations code...
     return someType;
}

Ответы [ 3 ]

5 голосов
/ 02 марта 2012

После наших комментариев, я думаю, я знаю, что вы хотите сейчас.

[Serializable]
    public class LazyLoadGetter : LocationInterceptionAspect, IInstanceScopedAspect
    {
        private object backing;

        public override void OnGetValue(LocationInterceptionArgs args)
        {
            if (backing == null)
            {
                args.ProceedGetValue();
                backing = args.Value;
            }

            args.Value = backing;
        }

        public object CreateInstance(AdviceArgs adviceArgs)
        {
            return this.MemberwiseClone();
        }

        public void RuntimeInitializeInstance()
        {

        }
    }

Тестовый код

public class test
    {
        [LazyLoadGetter]
        public int MyProperty { get { return LongOperation(); } }
    }
1 голос
/ 16 марта 2012

Благодаря ответу и комментариям DustinDavis я мог бы поработать над своей собственной реализацией, и я просто хотел поделиться этим, чтобы помочь другим людям.

Основные отличия отисходный ответ:

  • Реализация предложенного "только запустить операцию один раз" (цель блокировки)
  • Сделан статус инициализации поля поддержки более надежным, передав эту ответственностьa boolean.

Вот код:

[Serializable]
public class LazyLoadAttribute : LocationInterceptionAspect, IInstanceScopedAspect
{
    // Concurrent accesses management
    private readonly object _locker = new object();

    // the backing field where the loaded value is stored the first time.
    private object _backingField;

    // More reliable than checking _backingField for null as the result of the loading could be null.
    private bool _hasBeenLoaded = false;

    public override void OnGetValue(LocationInterceptionArgs args)
    {
        if (_hasBeenLoaded)
        {
            // Job already done
            args.Value = _backingField;
            return;
        }

        lock (_locker)
        {
            // Once the lock passed, we must check if the aspect has been loaded meanwhile or not.
            if (_hasBeenLoaded)
            {
                args.Value = _backingField;
                return;
            }

            // First call to the getter => need to load it.
            args.ProceedGetValue();

            // Indicate that we Loaded it
            _hasBeenLoaded = true;

            // store the result.
            _backingField = args.Value;
        }
    }

    public object CreateInstance(AdviceArgs adviceArgs)
    {
        return MemberwiseClone();
    }

    public void RuntimeInitializeInstance() { }

}
0 голосов
/ 16 января 2013

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

...