A Microsoft deveria ter implementado algo rápido para INotifyPropertyChanged
, como nas propriedades automáticas, basta especificar {get; set; notify;}
que acho que faz muito sentido fazê-lo. Ou existem complicações para fazer isso?
Nós mesmos podemos implementar algo como 'notificar' em nossas propriedades. Existe uma solução elegante para implementar INotifyPropertyChanged
em sua classe ou a única maneira de fazer isso é aumentando o PropertyChanged
evento em cada propriedade.
Caso contrário, podemos escrever algo para gerar automaticamente o trecho de código para gerar o PropertyChanged
evento?
Respostas:
Sem usar algo como postsharp, a versão mínima que eu uso usa algo como:
Cada propriedade é então algo como:
o que não é enorme; também pode ser usado como classe base, se você desejar. O
bool
retorno deSetField
informa se foi um no-op, caso você queira aplicar outra lógica.ou ainda mais fácil com o C # 5:
que pode ser chamado assim:
com o qual o compilador adicionará o
"Name"
automaticamente.O C # 6.0 facilita a implementação:
... e agora com o C # 7:
fonte
[CallerMemberName]
A partir do .Net 4.5, finalmente existe uma maneira fácil de fazer isso.
O .NET 4.5 apresenta um novo Atributo de Informação do Chamador.
Provavelmente, é uma boa ideia adicionar um comparador à função também.
Mais exemplos aqui e aqui
Consulte também Informações sobre chamadas (C # e Visual Basic)
fonte
Eu realmente gosto da solução de Marc, mas acho que ela pode ser um pouco melhorada para evitar o uso de uma "string mágica" (que não suporta refatoração). Em vez de usar o nome da propriedade como uma sequência, é fácil torná-la uma expressão lambda:
Basta adicionar os seguintes métodos ao código de Marc, ele fará o truque:
Aliás, isso foi inspirado
nesteURL atualizado dapostagem do blogfonte
Há também Fody, que tem um suplemento PropertyChanged , que permite escrever isso:
... e no momento da compilação injeta notificações alteradas de propriedade.
fonte
"Fody/.*?:",LogCustom2,True
destaca-a na cor "2 personalizadas". Eu o fiz rosa brilhante, para que seja fácil de encontrar. Apenas monte tudo, é a maneira mais legal de fazer qualquer coisa que possua muitas digitações repetitivas.Eu acho que as pessoas deveriam prestar um pouco mais de atenção ao desempenho; ele realmente afeta a interface do usuário quando há muitos objetos a serem vinculados (pense em uma grade com mais de 10.000 linhas) ou se o valor do objeto muda frequentemente (aplicativo de monitoramento em tempo real).
Peguei várias implementações encontradas aqui e em outros lugares e fiz uma comparação; confira a comparação de desempenho das implementações INotifyPropertyChanged .
Aqui está uma espiada no resultado
fonte
Apresento uma classe Bindable no meu blog em http://timoch.com/blog/2013/08/annoyed-with-inotifypropertychange/ O Bindable usa um dicionário como um pacote de propriedades. É fácil o suficiente adicionar as sobrecargas necessárias para uma subclasse gerenciar seu próprio campo de suporte usando parâmetros ref.
O código:
Pode ser usado assim:
fonte
protected T Get<T>(T defaultValue, [CallerMemberName] string name = null)
e também verificarif (_properties.ContainsKey(name) && Equals(value, Get<T>(default(T), name)))
em Set (para aumentar e economize quando primeiro conjunto para o valor padrão)Na verdade, ainda não tive a chance de tentar isso, mas da próxima vez que estiver configurando um projeto com um grande requisito para INotifyPropertyChanged, pretendo escrever um atributo Postsharp que injete o código no momento da compilação. Algo como:
Se tornará:
Não tenho certeza se isso funcionará na prática e preciso sentar e experimentar, mas não vejo por que não. Talvez seja necessário fazê-lo aceitar alguns parâmetros para situações em que mais de um OnPropertyChanged precisa ser acionado (se, por exemplo, eu tivesse uma propriedade FullName na classe acima)
Atualmente, estou usando um modelo personalizado no Resharper, mas mesmo com isso estou ficando cansado de todas as minhas propriedades serem tão longas.
Ah, uma rápida pesquisa no Google (o que eu deveria ter feito antes de escrever isso) mostra que pelo menos uma pessoa fez algo assim antes aqui . Não é exatamente o que eu tinha em mente, mas perto o suficiente para mostrar que a teoria é boa.
fonte
Sim, certamente existe uma maneira melhor. Aqui está:
O tutorial passo a passo encolheu por mim, com base neste artigo útil .
NotifierInterceptor
ProxyCreator
-
Coloque ligações no xaml:
Coloque a linha de código no arquivo code-behind MainWindow.xaml.cs assim:
DataContext = ProxyCreator.MakeINotifyPropertyChanged<MainViewModel>();
Atenção!!! Todas as propriedades delimitadas devem ser decoradas com a palavra-chave virtual porque elas foram usadas pelo proxy do castelo para substituir.
fonte
type
,interfaces to apply
,interceptors
.CreateClassProxy<T>
método genérico . Muito diferente ... hmmm, querendo saber por que tão limitado com o método genérico. :(Uma abordagem muito semelhante à AOP é injetar o material INotifyPropertyChanged em um objeto já instanciado em tempo real. Você pode fazer isso com algo como o Castle DynamicProxy. Aqui está um artigo que explica a técnica:
Adicionando INotifyPropertyChanged a um objeto existente
fonte
Olhe aqui : http://dotnet-forum.de/blogs/thearchitect/archive/2012/11/01/die-optimale-implementierung-des-inotifypropertychanged-interfaces.aspx
Está escrito em alemão, mas você pode baixar o ViewModelBase.cs. Todos os comentários no arquivo cs são escritos em inglês.
Com esta classe ViewModelBase, é possível implementar propriedades vinculáveis semelhantes às conhecidas Propriedades de Dependência:
fonte
Com base na resposta de Thomas, que foi adaptada de uma resposta de Marc, transformei o código alterado da propriedade refletida em uma classe base:
O uso é o mesmo que a resposta de Thomas, exceto que você pode passar propriedades adicionais a serem notificadas. Isso foi necessário para lidar com colunas calculadas que precisam ser atualizadas em uma grade.
Eu tenho isso dirigindo uma coleção de itens armazenados em um BindingList exposto por meio de um DataGridView. Isso eliminou a necessidade de fazer chamadas manuais de Refresh () para a grade.
fonte
Deixe-me apresentar minha própria abordagem chamada Yappi . Pertence aos geradores de classe derivados do proxy Runtime, adicionando nova funcionalidade a um objeto ou tipo existente, como o Dynamic Proxy do Caste Project.
Ele permite implementar INotifyPropertyChanged uma vez na classe base e depois declarar classes derivadas no seguinte estilo, ainda suportando INotifyPropertyChanged para novas propriedades:
A complexidade da construção de classe ou proxy derivada pode estar oculta atrás da seguinte linha:
E todo o trabalho de implementação INotifyPropertyChanged pode ser feito assim:
É totalmente seguro para refatoração, não usa reflexão após a construção do tipo e é rápido o suficiente.
fonte
TDeclaration
digitar o parâmetroPropertyImplementation
? Certamente você pode encontrar o tipo apropriado para chamar (não callvirt) o getter / setter apenas comTImplementation
?Todas essas respostas são muito legais.
Minha solução é usar os trechos de código para fazer o trabalho.
Isso usa a chamada mais simples para o evento PropertyChanged.
Salve esse snippet e use-o como usa o snippet 'fullprop'.
Você pode modificar a chamada como quiser (para usar as soluções acima)
fonte
Se você estiver usando dinâmica no .NET 4.5, não precisará se preocupar
INotifyPropertyChanged
.se Name estiver associado a algum controle, ele funcionará bem.
fonte
Outra solução combinada está usando o StackFrame:
Uso:
fonte
get_Foo
método no modo Release.Criei um método de extensão na minha biblioteca base para reutilização:
Isso funciona com o .Net 4.5 por causa do CallerMemberNameAttribute . Se você quiser usá-lo com uma versão .Net anterior, precisará alterar a declaração do método de:
...,[CallerMemberName] string propertyName = "", ...
para...,string propertyName, ...
Uso:
fonte
Eu resolvi dessa maneira (é um pouco trabalhoso, mas certamente é o mais rápido em tempo de execução).
No VB (desculpe, mas acho que não é difícil traduzi-lo em C #), faço essa substituição com o RE:
com:
Este transofrm todo o código como este:
No
E se eu quiser ter um código mais legível, posso ser o oposto fazendo a seguinte substituição:
Com
Eu lancei para substituir o código IL do método set, mas não posso escrever muito código compilado em IL ... Se um dia eu o escrever, eu direi!
fonte
Eu mantenho isso por aí como um trecho. O C # 6 adiciona uma sintaxe interessante para chamar o manipulador.
fonte
Aqui está uma versão Unity3D ou não CallerMemberName do NotifyPropertyChanged
Esse código permite que você escreva campos de apoio de propriedades como este:
Além disso, no re-afiador, se você criar um snippet de padrão / pesquisa, também poderá automatizar seu fluxo de trabalho, convertendo campos de objetos simples no suporte acima.
Padrão de Pesquisa:
Substituir Padrão:
fonte
Eu escrevi um artigo que ajuda com isso ( https://msdn.microsoft.com/magazine/mt736453 ). Você pode usar o pacote SolSoft.DataBinding NuGet. Então você pode escrever um código como este:
Benefícios:
fonte
Embora obviamente haja muitas maneiras de fazer isso, com exceção das respostas mágicas da AOP, nenhuma das respostas parece definir a propriedade de um modelo diretamente do modelo de exibição sem ter um campo local para referência.
O problema é que você não pode fazer referência a uma propriedade. No entanto, você pode usar uma Ação para definir essa propriedade.
Isso pode ser usado como a seguinte extração de código.
Confira este repositório do BitBucket para obter uma implementação completa do método e algumas maneiras diferentes de obter o mesmo resultado, incluindo um método que usa LINQ e um método que usa reflexão. Observe que esses métodos têm desempenho mais lento.
fonte
Outras coisas que você pode considerar ao implementar esses tipos de propriedades é o fato de que o INotifyPropertyChang * ed * ing usa classes de argumento de evento.
Se você tiver um grande número de propriedades que estão sendo definidas, o número de instâncias da classe de argumentos de eventos pode ser enorme, considere armazená-las em cache, pois são uma das áreas em que uma explosão de cadeia pode ocorrer.
Dê uma olhada nesta implementação e explique por que ela foi concebida.
Josh Smiths Blog
fonte
Acabei de encontrar o ActiveSharp - Automatic INotifyPropertyChanged , ainda não o usei, mas parece bom.
Para citar seu site ...
Em vez disso, escreva propriedades como esta:
Observe que não há necessidade de incluir o nome da propriedade como uma sequência. O ActiveSharp calcula isso de maneira confiável e correta. Funciona com base no fato de que sua implementação de propriedade passa o campo de apoio (_foo) por ref. (O ActiveSharp usa essa chamada "por ref" para identificar qual campo de apoio foi passado e, a partir do campo, identifica a propriedade).
fonte
Uma ideia usando reflexão:
fonte
Sei que essa pergunta já tem um zilhão de respostas, mas nenhuma delas parecia certa para mim. Meu problema é que não quero resultados de desempenho e estou disposto a tolerar um pouco de verbosidade apenas por esse motivo. Também não me importo muito com propriedades automáticas, o que me levou à seguinte solução:
Em outras palavras, a solução acima é conveniente se você não se importa em fazer isso:
Prós
Contras
Infelizmente, ainda é melhor do que fazer isso,
Para cada propriedade, o que se torna um pesadelo com a verbosidade adicional ;-(
Observe que não afirmo que esta solução seja melhor em termos de desempenho em comparação com as outras, apenas que é uma solução viável para quem não gosta das outras soluções apresentadas.
fonte
Eu vim com essa classe base para implementar o padrão observável, praticamente faz o que você precisa ( "automaticamente" implementando o conjunto e obtendo). Passei uma hora e uma hora nisso como protótipo, para que ele não tenha muitos testes de unidade, mas prova o conceito. Observe que ele usa o
Dictionary<string, ObservablePropertyContext>
para remover a necessidade de campos particulares.Aqui está o uso
fonte
Sugiro usar ReactiveProperty. Este é o método mais curto, exceto Fody.
em vez de
( DOCS )
fonte
Outra ideia...
fonte
=> aqui minha solução com os seguintes recursos
fonte
Usa isto
}
fonte