2009-05-14 6 views
12

Tenemos muchos objetos de valor inmutables en nuestro modelo de dominio, un ejemplo de esto es una posición, definida por una latitud, longitud & de altura.¿Cómo puedo editar objetos inmutables en WPF sin duplicar el código?

/// <remarks>When I grow up I want to be an F# record.</remarks> 
public class Position 
{ 
    public double Latitude 
    { 
     get; 
     private set; 
    } 

    // snip 

    public Position(double latitude, double longitude, double height) 
    { 
     Latitude = latitude; 
     // snip 
    } 
} 

La manera obvia de permitir la edición de una posición es la construcción de un modelo de vista que tiene getters y emisores, así como un método toPosition() para extraer la instancia posición inmutable validado. Si bien esta solución estaría bien, daría como resultado una gran cantidad de código duplicado, especialmente XAML.

Los objetos de valor en cuestión consisten en entre tres y cinco propiedades que son por lo general alguna variante de X, Y, Z & un poco de materia auxiliar. Dado esto, consideré la creación de tres ViewModels para manejar las diversas posibilidades, donde cada ViewModel necesitaría exponer propiedades para el valor de cada propiedad, así como una descripción para mostrar para cada etiqueta (por ejemplo, "Latitude").

Yendo más lejos, parece que podría simplificarlo a un modelo de vista general que puede tratar con N propiedades y conectar todo usando reflexión. Algo así como una grilla de propiedad, pero para objetos inmutables. Un problema con una cuadrícula de propiedad es que quiero ser capaz de cambiar el aspecto para que pueda tener etiquetas y cuadros de texto, tales como:

Latitude: [  32 ] <- TextBox 
Longitude: [  115 ] 
Height:  [  12 ] 

o ponerlo en una cuadrícula de datos tales como:

Latitude | Longitude | Height 
     32   115   12 

Así que mi pregunta es:

¿Puede pensar en una forma elegante de resolver este problema? ¿Hay bibliotecas que hacen esto o artículos sobre algo similar?

estoy buscando principalmente:

  • duplicación de código a ser minimizado
  • fácil añadir nuevos tipos de objetos de valor
  • posible extender con algún tipo de validación

Respuesta

2

Encontré esta vieja pregunta mientras investigaba mis posibles opciones en la misma situación. Pensé que debería actualizarlo en caso de que alguien más se tropiece con él:

Otra opción (no disponible cuando Paul ofreció su solución ya que .Net 4 aún no estaba fuera) es utilizar la misma estrategia, pero en lugar de implementar usando CustomTypeDescriptors, use una combinación de genéricos, objetos dinámicos y reflejo para lograr el mismo efecto.

En este caso, se define una clase

class Mutable<ImmutableType> : DynamicObject 
{ 
    //... 
} 

constructor Se necesita una instancia del tipo inmutable y un delegado que construye una nueva instancia de la misma fuera de un diccionario, al igual que en la respuesta de Pablo. La diferencia aquí, sin embargo, es que reemplaza el TryGetMember y TrySetMember para completar un diccionario interno que eventualmente va a utilizar como argumento para el constructor-delegado. Utiliza el reflejo para verificar que las únicas propiedades que acepta son las que realmente se implementan en ImmutableType.

En cuanto al rendimiento, apuesto a que la respuesta de Paul es más rápida, y no involucra objetos dinámicos, que son conocidos por poner a los desarrolladores de C# en ajustes. Pero la implementación de esta solución también es un poco más simple, porque los Descriptores de tipo son un poco arcanas.


Aquí está el solicitado la aplicación de prueba de concepto/ejemplo:

https://bitbucket.org/jwrush/mutable-generic-example

+0

Esta es una buena solución, prefiero a Paul ya que hay menos repetitivo. Por supuesto, también podría usar la reflexión en su solución para reducir la sobrecarga del programador de las propiedades de asignación al diccionario. En realidad, no hay una respuesta aceptada porque resolví el problema de otra manera y no había notado la respuesta de Paul más o menos 6 meses después de plantear la pregunta. Si tuviera que ofrecer un fragmento de código funcional que la gente pudiera usar, lo marcaré como la respuesta aceptada. –

0

¿Puedes pensar en una forma elegante de resolver este problema?

Honestamente, usted solo baila alrededor del problema, pero no mencione el problema en sí mismo;).

Si adivino correctamente su problema, la combinación de MultiBinding e IMultiValueConverter debería ser la solución.

HTH.

P.S. Por cierto, tienes instancias de clases inmutables, no objetos de valor. Con los objetos de valor (que se describen con la palabra clave struct) se bailaría mucho más, independientemente de si existían o no setters :).

+2

yo creo que hay una falta de comprensión de los objetos de valor. Los tipos de valores son estructuras como usted dice, pero un objeto de valor no es lo mismo. Consulte este artículo para obtener más información: http://www.lostechies.com/blogs/jimmy_bogard/archive/2008/05/20/entities-value-objects-aggregates-and-roots.aspx –

+1

¿Podría explicar este ¿Cómo puedo aclarar el problema? En pocas palabras, tengo un objeto que es inmutable, y me gustaría poder vincular fácilmente cuadros de texto a sus propiedades de la misma manera que lo hago con un objeto mutable. –

+0

Perdón por los objetos de valor: mi culpa, no sabía que había uno. Acerca de la elaboración del problema; por ejemplo, puede proporcionar uno de los recortes que desea generar. Tal vez, también sería útil si proporciona algunos metadatos de su visión de la solución genérica ideal en XAML. – archimed7592

5

Descriptores de tipo personalizado podrían ser utilizados para resolver este problema. Antes de vincularse a una posición, su descriptor de tipo podría activarse, y proporcionar métodos get y set para construir temporalmente los valores. Cuando se confirman los cambios, podría construir el objeto inmutable.

Podría ser algo como esto:

DataContext = new Mutable(position, 
    dictionary => new Position(dictionary["lattitude"], ...) 
); 

Sus fijaciones todavía puede tener este aspecto:

<TextBox Text="{Binding Path=Lattitude}" /> 

Debido a que el objeto mutable se 'fingir' que tienen propiedades como LAttitude gracias a su TypeDescriptor .

Alternativamente, puede utilizar un convertidor en sus enlaces y crear algún tipo de convención.

Su clase Mutable tomaría el objeto inmutable actual, y un Func<IDictionary, object> que le permite crear el nuevo objeto inmutable una vez que se completa la edición. Su clase Mutable haría uso del descriptor de tipo, que crearía PropertyDescriptors que crean el nuevo objeto inmutable al establecerse.

Para un ejemplo de cómo utilizar descriptores de tipo, ver aquí:

http://www.paulstovell.com/editable-object-adapter

Editar: si desea limitar la frecuencia se crean los objetos inmutables, también se podría hacer en BindingGroups y IEditableObject, que tu Mutable también puede implementar.

Cuestiones relacionadas