Является ли использование класса .Net Lazy излишним в этом случае? - PullRequest
3 голосов
/ 07 октября 2011

Я недавно узнал о ленивом классе в .Net и, вероятно, злоупотреблял им. У меня есть нижеприведенный пример, где вещи можно было бы оценить с нетерпением, но это привело бы к повторению одного и того же вычисления, если бы оно вызывалось снова и снова. В этом конкретном примере стоимость использования Lazy может не оправдать выгоду, и я не уверен в этом, поскольку я пока не понимаю, насколько дороги лямбды и ленивый вызов. Мне нравится использовать цепочечные свойства Lazy, потому что я могу разбивать сложную логику на маленькие, управляемые куски. Мне также больше не нужно думать о том, где лучше всего инициализировать вещи - все, что мне нужно знать, это то, что вещи не будут инициализированы, если я их не использую, и будут инициализированы ровно один раз, прежде чем я начну их использовать. Однако, как только я начал использовать lazy и lambdas, то, что было простым классом, теперь стало более сложным. Я не могу объективно решить, когда это оправдано, а когда это перебор с точки зрения сложности, читабельности, возможно, скорости. Какой будет ваша общая рекомендация?

    // This is set once during initialization.
    // The other 3 properties are derived from this one.
    // Ends in .dat
    public string DatFileName
    {
        get;
        private set;
    }

    private Lazy<string> DatFileBase
    {
        get
        {
            // Removes .dat
            return new Lazy<string>(() => Path.GetFileNameWithoutExtension(this.DatFileName));
        }
    }

    public Lazy<string> MicrosoftFormatName
    {
        get
        {
            return new Lazy<string>(() => this.DatFileBase + "_m.fmt");
        }
    }

    public Lazy<string> OracleFormatName
    {
        get
        {
            return new Lazy<string>(() => this.DatFileBase + "_o.fmt");
        }
    }

Ответы [ 5 ]

2 голосов
/ 07 октября 2011

Вероятно, это немного излишне.

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

Скорее всего, все, что вызывает ваши геттеры, будет нуждаться в действительном строковом значении сразу после вызова вашего геттера. Возвращать Lazy в таком случае не нужно, поскольку вызывающий код просто оценит экземпляр Lazy немедленно, чтобы получить то, что ему действительно нужно. «Ленивый» характер Lazy здесь теряется, и поэтому YAGNI (вам это не нужно).

Тем не менее, "накладные расходы", присущие Ленивым, не так уж и много. Lazy - это немного больше, чем класс, ссылающийся на лямбду, который будет генерировать универсальный тип. Лямбды относительно дешевы в определении и исполнении; это просто методы, которым при компиляции CLR присваивается имя mashup. Инстанциация дополнительного класса - главный кикер, и даже тогда это не страшно. Однако это не требует дополнительных затрат как с точки зрения кодирования, так и с точки зрения производительности.

2 голосов
/ 07 октября 2011

Вы сказали «Мне больше не нужно думать о том, где лучше всего инициализировать материал» .

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

Вы должны Lazy <> , когда есть объект, который необходимо передать, но требует некоторых вычислений.Таким образом, только когда он будет использоваться, он будет рассчитан.

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

1 голос
/ 15 июля 2012

Это, похоже, не использует Lazy<T> с целью сохранить создание / загрузку дорогого объекта настолько, насколько это (возможно, непреднамеренно) обернуть некоторый произвольный делегат для отложенного выполнения.Вероятно, вы хотите / намереваетесь вернуть получатели полученного свойства string, а не Lazy<string> объект.

Если вызывающий код выглядит как

string fileName = MicrosoftFormatName.Value;

, то, очевидно, существуетне имеет смысла, так как вы немедленно «Ленивая загрузка».

Если вызывающий код выглядит как

var lazyName = MicrosoftFormatName; // Not yet evaluated
// some other stuff, maybe changing the value of DatFileName
string fileName2 = lazyName.Value;

, то вы можете увидеть, что есть вероятность, что fileName2 не будет определенкогда создается объект lazyName.

Мне кажется, что Lazy<T> не лучше всего использовать для открытых свойств;здесь ваши геттеры возвращают новые (как в совершенно новых, отличных, дополнительных) Lazy<string> объектах, поэтому каждый вызывающий (потенциально) получит разных .Value!Все ваши свойства Lazy<string> зависят от того, установлен ли DatFileName в момент первого обращения к их .Value, поэтому вам будет всегда нужно думать о том, когда он инициализируется относительно использования каждого изпроизводные свойства.

См. статью MSDN " Ленивая инициализация ", в которой создается закрытая Lazy<T> вспомогательная переменная и метод получения открытого свойства, который выглядит следующим образом:

get { return _privateLazyObject.Value; }

Что я могу предположить, что ваш код должен / может понравиться, используя Lazy<string> для определения вашего базового свойства "set-Once":

// This is set up once (durinig object initialization) and
// evaluated once (the first time _datFileName.Value is accessed)
private Lazy<string> _datFileName = new Lazy<string>(() =>
    {
        string filename = null;
        //Insert initialization code here to determine filename
        return filename;
    });

// The other 3 properties are derived from this one.
// Ends in .dat
public string DatFileName
{
    get { return _datFileName.Value; }
    private set { _datFileName = new Lazy<string>(() => value); }
}

private string DatFileBase
{
    get { return Path.GetFileNameWithoutExtension(DatFileName); }
}

public string MicrosoftFormatName
{
    get { return DatFileBase + "_m.fmt"; }
}

public string OracleFormatName
{
    get { return DatFileBase + "_o.fmt"; }
}
0 голосов
/ 07 октября 2011

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

public class Artist
{
     public string Name { get; set; }
     public Lazy<Manager> Manager { get; internal set; }
}

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

0 голосов
/ 07 октября 2011

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

...