Реализация класса "LazyProperty" - это хорошая идея?

Я часто замечаю, что пишу свойство, которое лениво оценивается. Что-то вроде:

if (backingField == null) 
  backingField = SomeOperation();
return backingField;

Это небольшой код, но он часто повторяется, если у вас много свойств.

Я думаю об определении класса LazyProperty:

public class LazyProperty<T>
    {
    private readonly Func<T> getter;

    public LazyProperty(Func<T> getter)
    {
        this.getter = getter;
    }

    private bool loaded = false;
    private T propertyValue;

    public T Value
    {
        get
        {
            if (!loaded)
            {
                propertyValue = getter();
                loaded = true;
            }
            return propertyValue;
        }
    }

    public static implicit operator T(LazyProperty<T> rhs)
    {
        return rhs.Value;
    }
}

Это позволило бы мне инициализировать такое поле:

first = new LazyProperty<HeavyObject>(() => new HeavyObject { MyProperty = Value });

И тогда тело свойства можно было бы сократить до:

public HeavyObject First { get { return first; } }

Он будет использоваться большей частью компании, поскольку войдет в общую библиотеку классов, которая используется в большинстве наших продуктов.

Я не могу решить, хорошая это идея или нет. Я думаю, что у решений есть свои плюсы, например:

  • Меньше кода
  • Код красивее

С другой стороны, будет сложнее посмотреть на код и точно определить, что происходит, особенно если разработчик не знаком с классом LazyProperty.

Как вы думаете ? Это хорошая идея или мне следует отказаться от нее? Кроме того, является ли неявный оператор хорошей идеей, или вы бы предпочли явно использовать свойство Value, если вам следует использовать этот класс?

Мнения и предложения приветствуются :-)

Стоит ли изучать PHP в 2026-2027 годах?
Стоит ли изучать PHP в 2026-2027 годах?
Привет всем, сегодня я хочу высказать свои соображения по поводу вопроса, который я уже много раз получал в своем сообществе: "Стоит ли изучать PHP в...
Поведение ключевого слова "this" в стрелочной функции в сравнении с нормальной функцией
Поведение ключевого слова "this" в стрелочной функции в сравнении с нормальной функцией
В JavaScript одним из самых запутанных понятий является поведение ключевого слова "this" в стрелочной и обычной функциях.
Приемы CSS-макетирования - floats и Flexbox
Приемы CSS-макетирования - floats и Flexbox
Здравствуйте, друзья-студенты! Готовы совершенствовать свои навыки веб-дизайна? Сегодня в нашем путешествии мы рассмотрим приемы CSS-верстки - в...
Тестирование функциональных ngrx-эффектов в Angular 16 с помощью Jest
В системе управления состояниями ngrx, совместимой с Angular 16, появились функциональные эффекты. Это здорово и делает код определенно легче для...
Концепция локализации и ее применение в приложениях React ⚡️
Концепция локализации и ее применение в приложениях React ⚡️
Локализация - это процесс адаптации приложения к различным языкам и культурным требованиям. Это позволяет пользователям получить опыт, соответствующий...
Пользовательский скаляр GraphQL
Пользовательский скаляр GraphQL
Листовые узлы системы типов GraphQL называются скалярами. Достигнув скалярного типа, невозможно спуститься дальше по иерархии типов. Скалярный тип...
7
0
628
10
Перейти к ответу Данный вопрос помечен как решенный

Ответы 10

Мне нравится идея в том, что это намного меньше кода и более элегантно, но я был бы очень обеспокоен тем фактом, что становится трудно смотреть на него и рассказывать, что происходит. Единственный способ, которым я мог бы это считать, - это иметь соглашение для переменных, устанавливаемых "ленивым" способом, а также комментировать везде, где это используется. Теперь не будет компилятора или чего-то еще, что обеспечит соблюдение этих правил, так что все еще YMMV.

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

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

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

Если вы так думаете, то ничего не можете сделать из-за страха, что кто-то не поймет, что вы сделали

Вы можете написать учебник или что-то еще в центральном репозитории, у нас есть вики для таких заметок

В целом, я думаю, что это хорошая идея для реализации (не желая начинать споры о том, является ли ленивая загрузка хорошей идеей или нет)

Мне нравится ваше решение, поскольку оно очень умное, но я не думаю, что вы сильно выиграете, используя его. Ленивая загрузка частного поля в общедоступное свойство - определенно место, где код может быть продублирован. Однако мне всегда казалось, что это шаблон для использования, а не код, который нужно реорганизовать в обычное место.

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

В целом я приветствую вашу попытку и ценю ее ум, но предлагаю вам вернуться к исходному решению по причинам, указанным выше.

Конечно, вы, по крайней мере, хотели бы, чтобы LazyPropery<T> был типом значения, иначе вы добавили память и давление сборщика мусора для каждого «лениво загружаемого» свойства в вашей системе.

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

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

driis 13.01.2009 22:16

В этом случае я создаю Фрагмент кода Visual Studio. Я думаю, это то, что тебе действительно следует делать.

Например, когда я создаю элементы управления ASP.NET, у меня часто есть данные, которые много хранятся в ViewState, поэтому я создал такой фрагмент кода:

public Type Value
{
    get
    {
        if (ViewState["key"] == null)
            ViewState["key"] = someDefaultValue;
        return (Type)ViewState["key"];
    }
    set{ ViewState["key"] = value; }
}

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

Лично я не думаю, что класс LazyProperty как таковой предлагает достаточно ценности, чтобы оправдать его использование, особенно с учетом недостатков его использования для типов значений (как упомянул Кент). Если вам нужны другие функции (например, сделать его многопоточным), это может быть оправдано как класс ThreadSafeLazyProperty.

Что касается неявного свойства, мне больше нравится свойство «Значение». Это немного больше набора текста, но для меня это намного понятнее.

Ответ принят как подходящий

Просто чтобы быть чересчур педантичным:

Предлагаемое вами решение, чтобы избежать повторения кода:

private LazyProperty<HeavyObject> first = 
  new LazyProperty<HeavyObject>(() => new HeavyObject { MyProperty = Value });
public HeavyObject First { 
  get { 
    return first; 
  } 
}

На самом деле символов больше, чем код, который вы не хотели повторять:

private HeavyObject first;
public HeavyObject First { 
  get {
    if (first == null) first = new HeavyObject { MyProperty = Value };
    return first;
  }
}

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

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

driis 13.01.2009 22:36

Не делай этого вообще.

Как правило, использование такого типа ленивых инициализированных свойств является допустимым выбором дизайна в одном случае: когда SomeOperation(); является дорогостоящей операцией (с точки зрения ввода-вывода, например, когда требуется обращение к базе данных или с точки зрения вычислений), И когда вы уверены, что вы часто будете НЕ нужен доступ к нему.

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

Если вам хочется создать такую ​​абстракцию, это запах.

Он не спрашивает, являются ли поля с отложенной оценкой допустимым выбором (он понимает разницу, когда это так, а когда нет). Он спрашивает, является ли предлагаемое им решение хорошим дизайнерским выбором.

petr k. 13.01.2009 22:33

Что ж, он говорит, что делает много ленивых начинаний, что мне тоже кажется странным. Для большинства свойств не требуется ленивый inits imo.

Brian Rasmussen 14.01.2009 00:11

@petr, да я знаю, в чем был вопрос. Я только говорю, что это совершенно плохая идея и почему. Предупреждение, чтобы не порезаться, является правильным ответом на вопрос "как мне лучше порезаться?"

Krzysztof Kozmic 14.01.2009 02:23

Думаю, это интересная идея. Сначала я бы порекомендовал вам скрыть свойство Lazy Property от вызывающего кода. Вы не хотите просачиваться в модель своей предметной области, что оно ленивое. Что вы делаете с неявным оператором, так что оставьте это.

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

Другие вопросы по теме