2016-08-24 3 views
0

После нескольких раз мне нужно реализовать шаблон Lazy<T>, мне пришла в голову идея поместить весь шаблон в класс, чтобы скрыть Lazy-реализацию.Encapsulate Lazy <T>

В случае «Загрузка одноплодной объект конфигурации для моих услуг», я сделал что-то вроде этого:

public interface IConfigurationProvider<T> 
{ 
    T GetConfiguration(); 
} 

public abstract class SingletonConfigurationProvider<TConfiguration> : IConfigurationProvider<TConfiguration> 
{ 
    private static readonly Lazy<TConfiguration> _lazy = 
     new Lazy<TConfiguration>(_loadConfiguration); 

    private static Func<TConfiguration> _loadConfiguration; 

    private SingletonConfigurationProvider() { } 
    public SingletonConfigurationProvider(Func<TConfiguration> LoadConfig) 
    { 
     _loadConfiguration = LoadConfig; 
    } 

    public TConfiguration GetConfiguration() 
    { 
     return _lazy.Value; 
    } 
} 

Моя цель состоит в получении от «внешнего» простоты в этом:

public class ConfigTest : SingletonConfigurationProvider<ObjectTest> 
{ 
    public ConfigTest() 
     : base(Load) 
    { 
    } 

    public static ObjectTest Load() 
    { 
     return new ObjectTest() 
     { 
      testInt = 3, 
      testString = "Hi" 
     }; 
    } 
} 

точки являются:

  • она компилирует, но он ломает во время выполнения, с «TypeInitialization Exceptio n "говорит, что _loadConfiguration не может быть пустым. Это потому, что ленивый строится до конструктора SingletonConfigurationProvider?
  • Можно ли достичь того, чего я хочу, не нарушая семантики синглтона?
+3

Вы в основном строите абстракцию вокруг двух строк кода: статическое поле и геттер. Это действительно стоит? – Sinatr

+1

Проблема может быть в первой строке, где вы инициализируете _lazy, когда _loadConfiguration имеет значение NULL. Попробуйте переместить инициализацию в свой перегруженный конструктор. – Sarathy

+0

@Sarathy В этом случае _lazy не может быть только для чтения –

ответ

1

Абстракция, которую вы ищете, - это memoising function. В этом виде функции вы изменяете функцию геттера так, чтобы он реализовал шаблон «только один раз». Неподтвержденный код, но грубо говоря;

public Func<T> Memoize(Func<T> slowFunction) { 
    bool evaluated = false; 
    T value = default(T); 

    return() => { 
     if (!evaluated) { 
      value = slowFunction(); 
      evaluated = true; 
     } 

     return value; 
    }; 
} 

Итак, у вас теперь есть функция, которую вы можете использовать следующим образом;

Func<TConfiguration> onceOnlyLoad = Memoise(Load); 

Теперь вы можете вызвать onceOnlyLoad() столько раз, сколько вам нравится, и это будет только загрузить конфигурационный первый раз вы называете его.

Если вы хотите, вы можете использовать Lazy<T> внутренне для giv то же поведение. Вам решать.

Смежные вопросы