Pergunta

Sinto-me frequentemente escrever uma propriedade que é avaliada preguiçosamente. Algo como:

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

Não é muito código, mas ele não se repetiu muito se você tem um monte de propriedades.

Eu estou pensando sobre a definição de uma classe chamada 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;
    }
}

Esta me permitirá inicializar um campo como este:

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

E, em seguida, o corpo da propriedade poderia ser reduzido para:

public HeavyObject First { get { return first; } }

Este seria usado por mais da empresa, uma vez que iria entrar em uma biblioteca de classe comum compartilhada pela maioria dos nossos produtos.

Eu não posso decidir se isso é uma boa idéia ou não. Eu acho que as soluções tem alguns prós, como:

  • Menos de código
  • código Prettier

Em contrapartida, seria mais difícil de olhar o código e determinar exatamente o que acontece - especialmente se um desenvolvedor não está familiarizado com a classe LazyProperty.

O que você acha? Será esta uma boa ideia ou devo abandoná-la? Além disso, é o operador implícita uma boa idéia, ou se você preferir usar a propriedade Value explicitamente se você deve estar usando esta classe?

opiniões e sugestões são bem-vindas: -)

Foi útil?

Solução

Apenas a ser excessivamente pedante:

A sua solução proposta para código repetindo evitar:

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

É realmente mais caracteres do que o código que você não queria repetir:

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

Além disso, acho que a conversão implícita fez o código muito difícil de entender. Eu não teria imaginado que um método que simplesmente retorna primeiro, realmente acabar criando um HeavyObject. Eu, pelo menos, ter deixado cair a conversão implícita e voltou first.Value da propriedade.

Outras dicas

não fazê-lo em tudo.

Geralmente usando este tipo de propriedades inicializados preguiçosos é uma escolha de design válida em um caso: quando SomeOperation(); é uma operação cara (em termos de I / O, como quando se requer um DB atingido, ou computacionalmente) E quando você está certo muitas vezes você não vai precisar acessá-lo.

Dito isso, por padrão você deve ir para a inicialização ansioso, e quando profiler diz que é o gargalo, então alterá-lo para inicialização lenta.

Se você sentir vontade de criar esse tipo de abstração, é um cheiro.

Certamente você tinha, pelo menos, quer a LazyPropery<T> para ser um tipo de valor, a memória de outra forma que você adicionou e pressão GC para cada propriedade "preguiçosamente-carregado" em seu sistema.

Além disso, o que dizer-threaded vários cenários? Considere dois tópicos solicitando a propriedade ao mesmo tempo. Sem bloqueio, você poderia criar duas instâncias da propriedade subjacente. Para evitar o bloqueio, no caso comum, você iria querer fazer um bloqueio duplo verificado.

Eu prefiro o primeiro código, porque a) é um padrão tão comum com propriedades que eu entendo-lo imediatamente, e b) o ponto que levantou: que não há está escondido mágica que você tem que ir olhar para cima para entender onde e quando o valor está sendo obtido.

Eu gosto da idéia de que é muito menos código e mais elegante, mas eu estaria muito preocupado com o fato de que torna-se difícil olhar para ele e dizer o que está acontecendo. A única maneira que eu considero que é ter uma convenção para definir as variáveis ??usando o modo "preguiçoso", e também a comentar qualquer lugar ele é utilizado. Agora não vai ser um compilador ou qualquer coisa que vai impor essas regras, por isso ainda YMMV.

No final, para mim, decisões como esta resumem a quem é que vai estar a olhar para ele e a qualidade desses programadores. Se você pode confiar em seus colegas desenvolvedores para usá-lo direito e comentário bem, então vá para ele, mas se não, você é melhor fora de fazer isso de uma maneira facilmente compreendido e seguido. / meus 2cents

Eu não acho que se preocupar com um desenvolvedor não compreender é um bom argumento contra fazer algo assim ...

Se você acha isso, então você não podia fazer nada por medo de alguém não entender o que você fez

Você poderia escrever um tutorial ou algo em um repositório central, temos aqui um wiki para este tipo de notas

No geral, eu acho que é uma idéia boa implementação (não querendo iniciar um debate se lazyloading é uma boa idéia ou não)

O que fazer neste caso é que eu criar um Visual Studio trecho de código. Eu acho que é o que você realmente deve fazer.

Por exemplo, quando eu criar controles ASP.NET, eu muitas vezes têm dados que são armazenados no ViewState muito, então eu criei um trecho de código como este:

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

Desta forma, o código pode ser facilmente criados com apenas um pouco de trabalho (definindo o tipo, a chave, o nome e o valor padrão). É reutilizável, mas você não tem a desvantagem de uma peça complexa de código que outros desenvolvedores podem não entender.

Eu gosto de sua solução, pois é muito inteligente, mas eu não acho que você ganhar muito usando-o. Preguiçoso carregar um campo privado em uma propriedade pública é definitivamente um lugar onde o código pode ser duplicado. No entanto, esta sempre me pareceu como um padrão para usar ao invés de código que precisa ser reformulado em um lugar comum.

A sua abordagem pode tornar-se uma preocupação no futuro, se você fizer qualquer serialização. Também é mais confuso inicialmente para entender o que você está fazendo com o tipo personalizado.

No geral eu aplaudo a sua tentativa e apreciar a sua inteligência, mas gostaria de sugerir que você reverter para a sua solução original pelas razões expostas acima.

Pessoalmente, eu não acho que a classe LazyProperty como é oferece valor suficiente para justificar a usá-lo especialmente considerando as desvantagens de usá-lo para tipos de valor tem (como Kent mencionado). Se você precisava de outra funcionalidade (como tornando-multithreaded), pode ser justificada como uma classe ThreadSafeLazyProperty.

sobre a propriedade implícita, eu como a propriedade "Valor" melhor. É um pouco mais de digitação, mas muito mais claro para mim.

Eu acho que essa é uma ideia interessante. Primeiro eu recomendo que você ocultar a propriedade preguiçoso do código de chamada, Você não quer vazar em seu modelo de domínio que é preguiçoso. Que seu fazer com o operador implícito de modo manter isso.

Eu gosto de como você pode usar essa abordagem para alça e abstrair os detalhes de bloqueio, por exemplo. Se você fizer isso, então eu acho que há valor e mérito. Se você fizer bloqueio add atente para o padrão de bloqueio duplo é muito fácil de errar.

Você pode usar C # Iterators. O post seguinte explica um uso amostra e as vantagens na sua utilização.

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

Licenciado em: CC-BY-SA com atribuição
Não afiliado a StackOverflow
scroll top