2010-05-11 19 views
6

El acceso directo lenguajemanija repetitivo Código de Propiedad en C#

public string Code 
{ 
    get; 
    set; 
} 

ahorra un poco de escribir la hora de definir las propiedades triviales en C#.

Sin embargo, me encuentro escribiendo un código de propiedad muy repetitivo, no tan trivial, que sigue un patrón claro, p.

public string Code 
{ 
    get { return code; } 
    set 
    { 
     if (code != value) 
     { 
      code = value; 
      NotifyPropertyChanged("Code"); 
     } 
    } 
} 

Definitivamente puedo definir un fragmento de Visual Studio para reducir el tipeo. Sin embargo, si necesito agregar algo a mi patrón, tengo que volver y cambiar bastante código existente.

¿Hay un enfoque más elegante? ¿Es un fragmento la mejor manera de hacerlo?

ACTUALIZACIÓN:

Como una mejora rápida por ahora, he editado (después de hacer una copia de seguridad)

C: \ Archivos de programa \ Microsoft Visual Studio 10.0 \ VC# \ Fragmentos \ 1033 \ Refactoring \ EncapsulateField.snippet

(camino es para VS 2010)

para reflejar mi patrón actual. Ahora, la herramienta de refactorización incorporada usa mi plantilla para crear una propiedad desde un campo. Inconvenientes: cambio global para Visual Studio, no puede cambiar retroactivamente el código de propiedad existente.

Respuesta

7

Esto se conoce como Aspect Oriented Programming (AOP).

Scott Hanselman realizó recientemente una entrevista con el creador de LinFu, Philip Laureano sobre este tema. (link)

Existen varias herramientas de AOP, dependiendo de sus necesidades.

Y, por último, algunas implementaciones de una clase INotifyPropertyChanged utilizando las herramientas anteriores:

Actualización 2013: Como esta respuesta original que he encontrado otra solución que hace todo lo que necesito mucha facilidad.

PropertyChanged.Fody (anteriormente NotifyPropertyWeaver) es un tejedor de IL posterior a la compilación que insertará automáticamente el código de propiedad modificado. Esta es ahora mi solución preferida para INotifyPropertyChanged.

+0

+1 ¡Fácilmente la mejor y más adecuada respuesta! – Pat

-1

un patrón muy común. Fragmento está bien, pero chico, ¿no sería genial si MS creado un poco de azúcar sintáctica para las notificaciones de cambio de propiedad de automóviles ....

Algo así como ....

public string Code { get; setwithnotify; } 

Eso sería muy bonito.

+0

Sí que se ocuparía de este patrón particular, aunque todavía deja el problema general de la repetición de un patrón arbitrario - a ser posible con la posibilidad de insertar modificaciones para un determinado propiedad - sin resolver. –

+0

Esto no es una respuesta sino más bien un debate, por lo que sería mejor colocarlo como un comentario a la pregunta original. Además de eso, no creo que sea una buena idea incluir conceptos del framework en la sintaxis de C#. Una vez que INotifiyPropertyChange pasa de moda (basta con ver las propiedades de dependencia) nos queda un concepto heredado. – jpierson

2

No lo he hecho yo mismo, pero he visto que se utiliza un marco de inyección de dependencia para esta tarea en particular.

va y Googles

Ajaja!

Parece que, utilizando el Unity DI framework, puede inyectar el INotifyPropertyChanged en la propiedad automática. Eche un vistazo a esta gran publicación de blog: http://shecht.wordpress.com/2009/12/12/inotifypropertychanged-with-unity-interception-aop/

Esto me recuerda a HanselMinutes reciente, donde Scott está hablando con un hombre sobre Aspect Orientated Programming (AOP) donde este tipo de inyección es un lugar muy común.

+0

DI parece muy prometedor. Aunque no se menciona en la pregunta, estoy trabajando con Silverlight en este momento. Me pregunto si Unity 2.0 para Silverlight también es compatible con ese patrón (no sé lo que tuvieron que dejar de lado frente a la edición estándar). –

1

¡Odio escribir ese código!

En el pasado, para hacer frente a este problema, he implementado un generador de código para producir una clase partial con las definiciones de propiedad.

+0

He hecho la generación de código de modelos UML en el pasado. Ese es un gran enfoque cuando el proyecto es "lo suficientemente grande" como para garantizar un enfoque basado en modelos y la creación del generador de códigos. Con las herramientas de modelado cada vez más potentes/integradas, el umbral "lo suficientemente grande" sigue bajando. –

+0

@Eric: Personalmente, evitaría UML y toda su carga intelectual. Creo que su objetivo aquí es simple: las clases con propiedades que notifican cambian. Una solución simple podría ser una secuencia de comandos de PowerShell que le permite escribir: 'clase C {propiedad cadena Código; } 'y produce una clase parcial. Ya he hecho esto antes cuando también quería una interfaz fluida para la clase. –

3

Aquí hay algo que me burlé como ejercicio. Originalmente inspirado en Jon Skeet's blog post.

public static class ObjectExtensions { 
    public static string GetPropertyNameAndValue<T>(this T obj, out object value) { 
     System.Reflection.PropertyInfo[] objGetTypeGetProperties = obj.GetType().GetProperties(); 
     if(objGetTypeGetProperties.Length == 1) { 
      value = objGetTypeGetProperties[0].GetValue(obj, null); 
      return objGetTypeGetProperties[0].Name; 
     } else 
      throw new ArgumentException("object must contain one property"); 
    } 
} 

class Whatever { 
protected void ChangeProperty<T>(object property, T newValue, Action change) { 
    object value; 
    var name = property.GetPropertyNameAndValue(out value); 

    if(value == null && newValue != null || value != null && !value.Equals(newValue)) { 
     change(); 
     OnPropertyChanged(name); 
    } 
} 

private string m_Title; 
public string Title { 
    get { return m_Title; } 
    set {ChangeProperty(
       new { Title }, //This is used to dynamically retrieve the property name and value 
       value, // new value 
       () => m_Title = value); //lambda to change the value 
    } 
} 
} 

Esto es lo mejor que pude encontrar. El rendimiento del tiempo de ejecución puede ser bastante alto, pero no lo he probado.

Un poco de explicación sobre la solución anterior. new { Title } crea un objeto anónimo y debido a la proyección (introducido en .NET 3.5) el objeto recién creado tiene una sola propiedad llamada Title y el valor que es el valor de Title propiedad del objeto original.

GetPropertyNameAndValue es la función que hace todo el trabajo interesante: recupera el nombre y el valor de la propiedad del objeto anónimo. ChangeProperty realiza una comprobación de igualdad e invoca la lambda que realmente cambia la propiedad y también llama al NotifyPropertyChanged.

alternativa usted puede simplemente hacer un fragmento de esta manera:

<?xml version="1.0" encoding="utf-8" ?> 
<CodeSnippets xmlns="http://schemas.microsoft.com/VisualStudio/2005/CodeSnippet"> 
    <CodeSnippet Format="1.0.0"> 
     <Header> 
      <Title>propfullinotify</Title> 
      <Shortcut>propfullinotify</Shortcut> 
      <Description>Code snippet for property and backing field with INotifyPropertyChanged</Description> 
      <Author>Microsoft Corporation</Author> 
      <SnippetTypes> 
       <SnippetType>Expansion</SnippetType> 
      </SnippetTypes> 
     </Header> 
     <Snippet> 
      <Declarations> 
       <Literal> 
        <ID>type</ID> 
        <ToolTip>Property type</ToolTip> 
        <Default>int</Default> 
       </Literal> 
       <Literal> 
        <ID>property</ID> 
        <ToolTip>Property name</ToolTip> 
        <Default>MyProperty</Default> 
       </Literal> 
       <Literal> 
        <ID>field</ID> 
        <ToolTip>The variable backing this property</ToolTip> 
        <Default>myVar</Default> 
       </Literal> 
      </Declarations> 
      <Code Language="csharp"> 
     <![CDATA[private $type$ $field$; 

    public $type$ $property$ 
    { 
     get { return $field$;} 
     set { 
     if ($field$ != value) 
     { 
     $field$ = value; 
     if (PropertyChanged != null) PropertyChanged(this, new PropertyChangedEventArgs("$property$")); 
     } 
    } 
    } 
    $end$]]> 
      </Code> 
     </Snippet> 
    </CodeSnippet> 
</CodeSnippets> 
+0

Ese es un enfoque interesante para usar métodos de extensión, aunque no sigo completamente la implementación. ¿Qué significa la parte "nuevo {Título}, título"? ¿Por qué no se hace referencia al "valor"? –

+0

Hehe, es algo que Jon Skeet inventó hace mucho, y es una forma de obtener el nombre de la propiedad en tiempo de ejecución. 'value' que no se referencia es en realidad un error, debido a su extracción desde otro lugar. Lo arreglaré y ampliaré la solución. –

+0

Gracias por la aclaración. Sin duda, un enfoque interesante! –

-6

Para responder a la pregunta general sobre el código repetitiva, y no sólo el caso de notificación de cambio de propiedad:

Claro. Este es un caso perfecto para una macro.

¡Oh, espera! C# no tiene macros. Porque, por supuesto, las macros son malas y no son la mejor solución para ningún problema de programación.

A menos que inserte un preprocesador en su compilación.

+1

-1: No es útil. –

+1

No fue útil (y ni siquiera sería una buena sugerencia si la pregunta estaba en C++) –

0

Gracias a todos por sus respuestas. He votado a favor las respuestas útiles. Sin embargo, después de investigar bastante, he llegado a creer que la mejor respuesta (al menos para mí y la forma en que desarrollo el software) es modelar las clases en Visual Studio y usar T4 para generar código parciales que implementen la propiedad código.

Ver http://msdn.microsoft.com/en-us/library/ee329480.aspx

+0

Eso es más o menos lo que Jay Bazuzi sugirió, ¿no es así? – jpierson

Cuestiones relacionadas