public class MyClass
{
public object Prop1 { get; set; }
public object Prop2 { get; set; }
public object Prop3 { get; set; }
}
Suponha que eu tenho um objeto myObject
de MyClass
e eu preciso redefinir suas propriedades, é melhor criar um novo objeto ou transferir cada propriedade? Suponha que eu não tenho nenhum uso adicional com a instância antiga.
myObject = new MyClass();
ou
myObject.Prop1 = null;
myObject.Prop2 = null;
myObject.Prop3 = null;
c#
object-oriented
construction
Bells
fonte
fonte
Suppose I have an object myObject of MyClass and I need to reset its properties
- Se for necessário redefinir as propriedades do seu objeto ou se for útil modelar o domínio do problema, por que não criar umreset()
método para o MyClass. Veja a resposta de HarrisonPaine abaixoRespostas:
Instanciar um novo objeto é sempre melhor, então você tem 1 lugar para inicializar as propriedades (o construtor) e pode atualizá-lo facilmente.
Imagine que você adiciona uma nova propriedade à classe, prefere atualizar o construtor do que adicionar um novo método que também reinicializa todas as propriedades.
Agora, há casos em que você pode querer reutilizar um objeto, um caso em que uma propriedade é muito cara para reinicializar e você deseja mantê-la. Isso seria mais especializado, no entanto, e você teria métodos especiais para reinicializar todas as outras propriedades. Você ainda deseja criar um novo objeto às vezes até para esta situação.
fonte
myObject = new MyClass(oldObject);
. Embora isso implique uma cópia exata do objeto original em uma nova instância.new MyClass(oldObject)
é a melhor solução para os casos em que a inicialização é cara.Você definitivamente deve preferir criar um novo objeto no grande maioria dos casos. Problemas com a reatribuição de todas as propriedades:
A
e classeB
são instâncias passadas da classeC
, elas precisam saber se alguma vez serão passadas à mesma instância e, se sim, se a outra ainda a está usando. Isso une fortemente classes que, de outra forma, não têm razão de existir.Fraction
classe com um numerador e denominador e queria reforçar que ela sempre fosse reduzida (ou seja, o MDC do numerador e denominador era 1). Isso é impossível de fazer bem, se você deseja permitir que as pessoas definam publicamente o numerador e o denominador, pois elas podem passar por um estado inválido para passar de um estado válido para outro. Por exemplo, 1/2 (válido) -> 2/2 (inválido) -> 2/3 (válido).Todos esses são problemas bastante significativos. E o que você recebe em troca do trabalho extra que você cria é ... nada. A criação de instâncias de objetos é, em geral, incrivelmente barata, portanto o benefício de desempenho quase sempre será totalmente desprezível.
Como a outra resposta mencionada, o único momento em que o desempenho pode ser uma preocupação relevante é se a sua turma fizer algum trabalho significativamente caro na construção. Mas, mesmo nesse caso, para que essa técnica funcione, você precisará separar a parte cara das propriedades que está redefinindo, para poder usar o padrão flyweight ou similar.
Como uma observação lateral, alguns dos problemas acima podem ser mitigados de certa forma por não usar setters e, em vez
public Reset
método em sua classe que os mesmos parâmetros do construtor. Se, por algum motivo, você quiser seguir essa rota de redefinição, provavelmente seria uma maneira muito melhor de fazê-lo.Ainda assim, a complexidade e repetição adicionais que adicionam, juntamente com os pontos acima que ela não aborda, ainda são um argumento muito convincente contra fazê-lo, especialmente quando pesadas em relação aos benefícios inexistentes.
fonte
Dado o exemplo muito genérico, é difícil dizer. Se "redefinir as propriedades" fizer sentido semântico no caso do domínio, fará mais sentido para o consumidor da sua classe chamar
Do que
NUNCA exigiria que o consumidor ligasse para sua classe
Se a classe representa algo que pode ser redefinido, ela deve expor essa funcionalidade por meio de um
Reset()
método, em vez de depender da chamada do construtor ou da configuração manual de suas propriedades.fonte
Como sugerem Harrison Paine e Brandin, eu reutilizaria o mesmo objeto e fatoraria a inicialização das propriedades em um método Reset:
fonte
Se o padrão de uso pretendido para uma classe é que um único proprietário manterá uma referência para cada instância, nenhum outro código manterá cópias das referências e será muito comum que os proprietários tenham loops que precisam, muitas vezes , " preencha "uma instância em branco, use-a temporariamente e nunca mais precise dela (uma classe comum que atenda a esse critério seria
StringBuilder
), pode ser útil do ponto de vista de desempenho para a classe incluir um método para redefinir uma instância para Nova Condição. É provável que essa otimização não valha muito se a alternativa exigir apenas a criação de algumas centenas de instâncias, mas o custo de criação de milhões ou bilhões de instâncias de objetos pode aumentar.Em uma nota relacionada, existem alguns padrões que podem ser usados para métodos que precisam retornar dados em um objeto:
O método cria um novo objeto; retorna referência.
O método aceita uma referência a um objeto mutável e o preenche.
O método aceita uma variável do tipo de referência como
ref
parâmetro e usa o objeto existente, se adequado, ou altera a variável para identificar um novo objeto.A primeira abordagem é geralmente a mais fácil semanticamente. O segundo é um pouco estranho no lado da chamada, mas pode oferecer melhor desempenho se um chamador frequentemente conseguir criar um objeto e usá-lo milhares de vezes. A terceira abordagem é um pouco estranha semântica, mas pode ser útil se um método precisar retornar dados em uma matriz e o chamador não souber o tamanho da matriz necessária. Se o código de chamada contiver a única referência à matriz, a reescrita dessa referência com uma referência a uma matriz maior será semanticamente equivalente a simplesmente aumentar a matriz (que é o comportamento desejado semanticamente). Embora o uso
List<T>
possa ser mais agradável do que o uso de uma matriz redimensionada manualmente em muitos casos, as matrizes de estruturas oferecem melhor semântica e melhor desempenho do que as listas de estruturas.fonte
Acho que a maioria das pessoas que responde a favor da criação de um novo objeto está perdendo um cenário crítico: coleta de lixo (GC). O GC pode ter um impacto real no desempenho em aplicativos que criam muitos objetos (jogos de reflexão ou aplicativos científicos).
Digamos que eu tenho uma árvore de expressão que representa uma expressão matemática, onde nos nós internos existem nós de função (ADD, SUB, MUL, DIV) e os nós folha são nós terminais (X, e, PI). Se minha classe de nó tiver um método Evaluate (), provavelmente chamará recursivamente os filhos e coletará dados por meio de um nó Data. No mínimo, todos os nós das folhas precisarão criar um objeto Data e, em seguida, eles poderão ser reutilizados no caminho até a árvore até que o valor final seja avaliado.
Agora, digamos que tenho milhares dessas árvores e as avalio em um loop. Todos esses objetos de dados acionam um GC e causam um grande impacto no desempenho (perda de até 40% na utilização da CPU para algumas execuções no meu aplicativo - executei um criador de perfil para obter os dados).
Uma solução possível? Reutilize esses objetos de dados e chame alguns .Reset () depois que você terminar de usá-los. Os nós folha não chamarão mais 'new Data ()', eles chamarão algum método Factory que manipula o ciclo de vida do objeto.
Eu sei disso porque tenho um aplicativo que atingiu esse problema e resolveu o problema.
fonte