Реализация класса "LazyProperty" - это хорошая идея? - PullRequest
7 голосов
/ 13 января 2009

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

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, если вам следует использовать этот класс?

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

Ответы [ 11 ]

7 голосов
/ 13 января 2009

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

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

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. Я бы по крайней мере отбросил неявное преобразование и вернул бы first.Value из свойства.

5 голосов
/ 13 января 2009

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

Как правило, использование этого типа отложенных инициализированных свойств является допустимым выбором проекта в одном случае: когда SomeOperation(); является дорогостоящей операцией (с точки зрения ввода-вывода, например, когда требуется обращение к БД, или в вычислительном отношении) И когда вы Вы наверняка НЕ ​​ДОЛЖНЫ получать к нему доступ.

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

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

4 голосов
/ 13 января 2009

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

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

2 голосов
/ 13 января 2009

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

1 голос
/ 13 января 2009

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

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

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

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

1 голос
/ 13 января 2009

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

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

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

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

1 голос
/ 13 января 2009

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

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

0 голосов
/ 13 ноября 2009

Вы можете использовать итераторы C #. Следующий пост объясняет пример использования и преимущества его использования.

http://hemanshubhojak.com/Home/Post?postId=3

0 голосов
/ 13 января 2009

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

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

0 голосов
/ 13 января 2009

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

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

...