Microsoft debería haber implementado algo rápido INotifyPropertyChanged
, como en las propiedades automáticas, solo especifique {get; set; notify;}
Creo que tiene mucho sentido hacerlo. ¿O hay alguna complicación para hacerlo?
¿Podemos nosotros mismos implementar algo como 'notificar' en nuestras propiedades? ¿Existe una solución elegante para implementar INotifyPropertyChanged
en su clase o la única forma de hacerlo es elevando el PropertyChanged
evento en cada propiedad?
Si no, ¿podemos escribir algo para generar automáticamente el fragmento de código para generar el PropertyChanged
evento?
Respuestas:
Sin usar algo como postsharp, la versión mínima que uso usa algo como:
Cada propiedad es entonces algo así como:
que no es enorme; También se puede utilizar como clase base si lo desea. El
bool
retorno deSetField
te dice si fue un no-op, en caso de que quieras aplicar otra lógica.o incluso más fácil con C # 5:
que se puede llamar así:
con el cual el compilador agregará el
"Name"
automáticamente.C # 6.0 facilita la implementación:
... y ahora con C # 7:
fuente
[CallerMemberName]
A partir de .Net 4.5 finalmente hay una manera fácil de hacer esto.
.Net 4.5 presenta un nuevo atributo de información de llamadas.
Probablemente sea una buena idea agregar un comparador a la función también.
Más ejemplos aquí y aquí.
Consulte también Información de la persona que llama (C # y Visual Basic)
fuente
Realmente me gusta la solución de Marc, pero creo que se puede mejorar ligeramente para evitar el uso de una "cadena mágica" (que no admite la refactorización). En lugar de usar el nombre de la propiedad como una cadena, es fácil convertirlo en una expresión lambda:
Simplemente agregue los siguientes métodos al código de Marc, hará el truco:
Por cierto, esto se inspiró en
estaURL actualizada de lapublicación del blogfuente
También está Fody, que tiene un complemento PropertyChanged , que le permite escribir esto:
... y en tiempo de compilación inyecta notificaciones de propiedades modificadas.
fuente
"Fody/.*?:",LogCustom2,True
resalta en el color "Personalizado 2". Lo hice de color rosa brillante para que sea fácil de encontrar. Simplemente Fody todo, es la mejor manera de hacer cualquier cosa que tenga muchos tipeos repetitivos.Creo que la gente debería prestar un poco más de atención al rendimiento; realmente impacta la interfaz de usuario cuando hay muchos objetos a vincular (piense en una cuadrícula con más de 10,000 filas), o si el valor del objeto cambia con frecuencia (aplicación de monitoreo en tiempo real).
Tomé varias implementaciones encontradas aquí y en otros lugares e hice una comparación; échale un vistazo a la comparación de rendimiento de las implementaciones INotifyPropertyChanged .
Aquí hay un vistazo al resultado
fuente
Presento una clase Bindable en mi blog en http://timoch.com/blog/2013/08/annoyed-with-inotifypropertychange/ Bindable usa un diccionario como una bolsa de propiedades. Es bastante fácil agregar las sobrecargas necesarias para que una subclase administre su propio campo de respaldo utilizando parámetros de referencia.
El código:
Se puede usar así:
fuente
protected T Get<T>(T defaultValue, [CallerMemberName] string name = null)
y también verificarif (_properties.ContainsKey(name) && Equals(value, Get<T>(default(T), name)))
en Establecer (para subir y guardar cuando se establece por primera vez en el valor predeterminado)Todavía no he tenido la oportunidad de probar esto, pero la próxima vez que esté configurando un proyecto con un gran requisito para INotifyPropertyChanged tengo la intención de escribir un atributo Postsharp que inyectará el código en el momento de la compilación. Algo como:
Se convertirá:
No estoy seguro de si esto funcionará en la práctica y necesito sentarme y probarlo, pero no veo por qué no. Es posible que deba hacer que acepte algunos parámetros para situaciones en las que se deba activar más de OnPropertyChanged (si, por ejemplo, tuviera una propiedad FullName en la clase anterior)
Actualmente estoy usando una plantilla personalizada en Resharper, pero incluso con eso me estoy hartando de que todas mis propiedades sean tan largas.
Ah, una búsqueda rápida en Google (que debería haber hecho antes de escribir esto) muestra que al menos una persona ha hecho algo como esto antes aquí . No es exactamente lo que tenía en mente, pero lo suficientemente cerca como para mostrar que la teoría es buena.
fuente
Sí, ciertamente existe una mejor manera. Aquí está:
Tutorial paso a paso reducido por mí, basado en este útil artículo .
NotifierInterceptor
ProxyCreator
-
Poner enlaces en xaml:
Ponga una línea de código en el archivo de código subyacente MainWindow.xaml.cs de esta manera:
DataContext = ProxyCreator.MakeINotifyPropertyChanged<MainViewModel>();
¡¡¡Atención!!! Todas las propiedades delimitadas deben decorarse con la palabra clave virtual porque las usa el proxy del castillo para anularlas.
fuente
type
,interfaces to apply
,interceptors
.CreateClassProxy<T>
método genérico . Muy diferente ... hmmm, preguntándome por qué tan limitado con el método genérico. :(Un enfoque muy parecido a AOP es inyectar el material INotifyPropertyChanged en un objeto ya instanciado sobre la marcha. Puedes hacer esto con algo como Castle DynamicProxy. Aquí hay un artículo que explica la técnica:
Agregar INotifyPropertyChanged a un objeto existente
fuente
Mire aquí: http://dotnet-forum.de/blogs/thearchitect/archive/2012/11/01/die-optimale-implementierung-des-inotifypropertychanged-interfaces.aspx
Está escrito en alemán, pero puede descargar ViewModelBase.cs. Todos los comentarios en el archivo cs están escritos en inglés.
Con esta clase ViewModelBase, es posible implementar propiedades enlazables similares a las conocidas propiedades de dependencia:
fuente
Basado en la respuesta de Thomas, que fue adaptada de una respuesta de Marc, he convertido el código modificado de propiedad reflectante en una clase base:
El uso es el mismo que el de Thomas, excepto que puede pasar propiedades adicionales para notificar. Esto fue necesario para manejar las columnas calculadas que deben actualizarse en una cuadrícula.
Tengo esto impulsando una colección de elementos almacenados en una BindingList expuesta a través de un DataGridView. Me ha eliminado la necesidad de hacer llamadas manuales Refresh () a la grilla.
fuente
Permítanme presentarles mi propio enfoque llamado Yappi . Pertenece a los generadores de clases derivados del proxy de tiempo de ejecución, que agregan nuevas funciones a un objeto o tipo existente, como el proxy dinámico de Caste Project.
Permite implementar INotifyPropertyChanged una vez en la clase base, y luego declarar clases derivadas en el siguiente estilo, aún admitiendo INotifyPropertyChanged para nuevas propiedades:
La complejidad de la construcción de clase o proxy derivada se puede ocultar detrás de la siguiente línea:
Y todo el trabajo de implementación de INotifyPropertyChanged se puede hacer así:
Es completamente seguro para la refactorización, no utiliza reflejos después de la construcción tipo y es lo suficientemente rápido.
fuente
TDeclaration
parámetro typePropertyImplementation
? ¿Seguramente puede encontrar el tipo apropiado para llamar (no callvirt) al getter / setter solo conTImplementation
?Todas estas respuestas son muy bonitas.
Mi solución es usar los fragmentos de código para hacer el trabajo.
Esto utiliza la llamada más simple al evento PropertyChanged.
Guarde este fragmento y úselo mientras usa el fragmento 'fullprop'.
Puede modificar la llamada como desee (para usar las soluciones anteriores)
fuente
Si está utilizando dinámicas en .NET 4.5, no necesita preocuparse
INotifyPropertyChanged
.si Name está sujeto a algún control, simplemente funciona bien.
fuente
Otra solución combinada es usar StackFrame:
Uso:
fuente
get_Foo
método en modo Release.Creé un método de extensión en mi biblioteca base para reutilizarlo:
Esto funciona con .Net 4.5 debido a CallerMemberNameAttribute . Si desea usarlo con una versión anterior de .Net, debe cambiar la declaración del método de:
...,[CallerMemberName] string propertyName = "", ...
a...,string propertyName, ...
Uso:
fuente
Resolví de esta manera (es un poco laborioso, pero seguramente es el más rápido en tiempo de ejecución).
En VB (lo siento, pero creo que no es difícil traducirlo en C #), hago esta sustitución con RE:
con:
Esto transforma todo el código de esta manera:
En
Y si quiero tener un código más legible, puedo ser lo contrario simplemente haciendo la siguiente sustitución:
Con
Lanzo para reemplazar el código IL del método establecido, pero no puedo escribir mucho código compilado en IL ... ¡Si algún día lo escribo, te lo diré!
fuente
Mantengo esto como un fragmento. C # 6 agrega una buena sintaxis para invocar el controlador.
fuente
Aquí hay una versión Unity3D o no CallerMemberName de NotifyPropertyChanged
Este código le permite escribir campos de respaldo de propiedad como este:
Además, en Reorganizador, si crea un fragmento de patrón / búsqueda, también puede automatizar su flujo de trabajo al convertir simples campos de apoyo en el respaldo anterior.
Patrón de búsqueda:
Reemplazar patrón:
fuente
He escrito un artículo que ayuda con esto ( https://msdn.microsoft.com/magazine/mt736453 ). Puede usar el paquete SolSoft.DataBinding NuGet. Entonces puedes escribir código como este:
Beneficios:
fuente
Aunque obviamente hay muchas maneras de hacer esto, con la excepción de las respuestas mágicas de AOP, ninguna de las respuestas parece enfocarse en establecer la propiedad de un Modelo directamente desde el modelo de vista sin tener un campo local para referencia.
El problema es que no puede hacer referencia a una propiedad. Sin embargo, puede usar una Acción para establecer esa propiedad.
Esto se puede usar como el siguiente extracto de código.
Consulte este repositorio de BitBucket para obtener una implementación completa del método y algunas formas diferentes de lograr el mismo resultado, incluido un método que usa LINQ y un método que usa reflexión. Tenga en cuenta que estos métodos son más lentos en cuanto al rendimiento.
fuente
Otras cosas que quizás desee considerar al implementar este tipo de propiedades es el hecho de que INotifyPropertyChang * ed * ing usa clases de argumentos de eventos.
Si tiene una gran cantidad de propiedades que se están configurando, la cantidad de instancias de clase de argumento de evento puede ser enorme, debe considerar almacenarlas en caché, ya que son una de las áreas en las que puede producirse una explosión de cadena.
Eche un vistazo a esta implementación y explique por qué fue concebida.
Blog de Josh Smiths
fuente
Acabo de encontrar ActiveSharp - Automatic INotifyPropertyChanged , todavía tengo que usarlo, pero se ve bien.
Para citar de su sitio web ...
En cambio, escriba propiedades como esta:
Tenga en cuenta que no es necesario incluir el nombre de la propiedad como una cadena. ActiveSharp lo determina de manera confiable y correcta por sí mismo. Funciona en función del hecho de que la implementación de su propiedad pasa el campo de respaldo (_foo) por ref. (ActiveSharp usa esa llamada "por referencia" para identificar qué campo de respaldo se pasó y desde el campo identifica la propiedad).
fuente
Una idea usando la reflexión:
fuente
Me doy cuenta de que esta pregunta ya tiene muchísimas respuestas, pero ninguna de ellas me pareció correcta. Mi problema es que no quiero ningún éxito en el rendimiento y estoy dispuesto a soportar un poco de verbosidad solo por esa razón. Tampoco me importan demasiado las propiedades automotrices, lo que me llevó a la siguiente solución:
En otras palabras, la solución anterior es conveniente si no le importa hacer esto:
Pros
Contras
Por desgracia, todavía es mejor que hacer esto,
Para cada propiedad, lo que se convierte en una pesadilla con la verbosidad adicional ;-(
Tenga en cuenta que no afirmo que esta solución sea mejor en términos de rendimiento en comparación con las otras, solo que es una solución viable para aquellos a quienes no les gustan las otras soluciones presentadas.
fuente
Se me ocurrió esta clase base para implementar el patrón observable, más o menos hace lo que necesita ( "automáticamente" implementando el conjunto y obtener). Pasé una hora en línea como prototipo, por lo que no tiene muchas pruebas unitarias, pero prueba el concepto. Tenga en cuenta que utiliza
Dictionary<string, ObservablePropertyContext>
para eliminar la necesidad de campos privados.Aquí está el uso
fuente
Sugiero usar ReactiveProperty. Este es el método más corto, excepto Fody.
en lugar
( DOCS )
fuente
Otra idea...
fuente
=> aquí mi solución con las siguientes características
fuente
Utilizar este
}
fuente