Методы ленивой инициализации со свойствами
-
26-09-2019 - |
Вопрос
В настоящее время я изменяю широко используемый класс для перемещения как большую часть дорогой инициализации от конструктора класса в ленивые инициализированные свойства. Ниже приведен пример (в C #):
До:
public class ClassA
{
public readonly ClassB B;
public void ClassA()
{
B = new ClassB();
}
}
После:
public class ClassA
{
private ClassB _b;
public ClassB B
{
get
{
if (_b == null)
{
_b = new ClassB();
}
return _b;
}
}
}
В классе я есть яркость из этих свойств в классе, и некоторые не используются в определенных контекстах (отсюда, лень), но если они используются, они могут быть вызваны неоднократно.
К сожалению, свойства часто используются внутри класса. Это означает, что есть потенциал для частной переменной (_B), которая будет использоваться непосредственно методом без него инициализации.
Есть ли способ сделать только общественное свойство (B), доступное внутри класса, или даже альтернативный метод с тем же инициализированным - при необходимости?
Это подтверждается из программистов (не достаточно субъективно):https://softwareEngineering.stackexchange.com/Questions/34270/best-methods-for-lazy-initialization-with-properties.
Решение
Вы можете рассмотреть возможность толкания ленивых свойств в базовый класс, чтобы избежать прямого доступа к ведущей переменной. Не идеально, я знаю. Я всегда думал, что это что-то не хватало в C # IE непосредственную поддержку ленивых свойств.
Другие советы
Ну, мое рекомендуемое решение было бы сказать, чтобы ваш сотрудник использовать свойство, а не поле. Но вы можете имитировать его до некоторой степени, как это:
public class ClassA
{
private Lazy<ClassB> _b = new Lazy<ClassB>(() => new ClassB());
public ClassB B
{
get
{
return _b.Value;
}
}
}
Теперь довольно трудно прикрутить.
@Cibacity posted (и впоследствии) удалил [и позже восстановленный: P] альтернативный вариант, используя абстрактный базовый класс. Хотя он не может быть идеальным с точки зрения распространения кода, он обеспечивает хорошую инкапсуляцию, удаляющую множество кодовых загрязнителей для очистительного и более сжатого класса. Например, вы могли бы рассмотреть возможность объединения методов для достижения обоих целей:
public class ClassB { /* Class to be lazily instantiated */ }
public abstract class BaseA
{
private Lazy<ClassB> _b = new Lazy<ClassB>(() => new ClassB());
public virtual ClassB B { get { return _b.Value; } }
}
public class ClassA : BaseA
{
public override ClassB B { get { return base.B; } }
}
На первый взгляд, похоже, что это более длительное, но когда вы считаете, что класс, который является классом, в котором вы будете работать и с, теперь это означает, что все ваши ссылки проходят через одно и то же свойство - нет посторонних ненужных Поле, вызывающее потенциальную путаницу, нет в обход недвижимости для справки _B напрямую, и нет необходимости рассказывать вашему коллеги, который использовать ... есть только один.
Не говоря уже, что это правильный способ сделать это или что это шаблон, который должен или не следует следовать, я просто указываю на преимущества того, что предложил @Chibacity, что в противном случае может остаться незамеченным.
Было бы здорово, если бы у вас были неявные ленивые нагруженные свойства без необходимости ссылаться на B.Value ... Например:
[Lazy]
public ClassB B { get; }
или для объектов без параметров конструкторов
[Lazy(() => new ClassB("Hello", "World"))]
public ClassB B { get; }
или, возможно, как @Chibacity предлагается в комментарии
public ClassB B { lazyget; }
или
public ClassB B { lazyget : new ClassB(); }
Увы, я не думаю, что ни одно из них доступны в настоящее время решениями в любой форме ...