visual test studio how development define debug c# c-preprocessor

test - if development c#



Preprocesador C# (11)

¿Debo construir un preprocesador C #? ¿Hay uno disponible que haga las cosas simples que quiero hacer?

Siempre puede usar el preprocesador C: C # está lo suficientemente cerca, sintaxis. M4 también es una opción.

Si bien la especificación C # incluye un preprocesador y directivas básicas (#define, #if, etc.), el idioma no tiene el mismo preprocesador flexible que se encuentra en lenguajes como C / C ++. Creo que la falta de un preprocesador tan flexible fue una decisión de diseño hecha por Anders Hejlsberg (aunque, desafortunadamente, no puedo encontrar una referencia a esto ahora). Por experiencia, esta es sin duda una buena decisión, ya que hubo algunas macros realmente imposibles de mantener creadas cuando estaba haciendo mucho C / C ++.

Dicho esto, hay una serie de escenarios en los que podría encontrar un preprocesador un poco más flexible para ser útil. Código como el siguiente podría mejorarse mediante algunas directivas simples de preprocesador:

public string MyProperty { get { return _myProperty; } set { if (value != _myProperty) { _myProperty = value; NotifyPropertyChanged("MyProperty"); // This line above could be improved by replacing the literal string with // a pre-processor directive like "#Property", which could be translated // to the string value "MyProperty" This new notify call would be as follows: // NotifyPropertyChanged(#Property); } } }

¿Sería una buena idea escribir un pre-procesador para manejar casos extremadamente simples como este? Steve McConnell escribió en Code Complete (p208):

Escriba su propio preprocesador Si un idioma no incluye un preprocesador, es bastante fácil escribir uno ...

Estoy dividido. Fue una decisión de diseño dejar un pre-procesador tan flexible fuera de C #. Sin embargo, un autor que respeto mucho menciona que puede estar bien en algunas circunstancias.

¿Debo construir un preprocesador C #? ¿Hay uno disponible que haga las cosas simples que quiero hacer?


Considere echar un vistazo a una solución orientada a aspectos como PostSharp , que inyecta código después del hecho en función de los atributos personalizados. Es lo opuesto a un precompilador, pero puede darle el tipo de funcionalidad que está buscando (notificaciones PropertyChanged, etc.).


Si estuviera diseñando la próxima versión de C #, pensaría en cada función que tiene una variable local incluida automáticamente que contiene el nombre de la clase y el nombre de la función. En la mayoría de los casos, el optimizador del compilador lo eliminaría.

Aunque no estoy seguro de que haya mucha demanda para ese tipo de cosas.


@Jorge escribió: Si quiere procesar su código de una manera diferente, use una directiva de preprocesador, pero si solo quiere un fragmento de código, busque otra forma porque el preprocesador no estaba destinado a hacer eso.

Interesante. Realmente no considero que un preprocesador necesariamente funcione de esta manera. En el ejemplo proporcionado, estoy haciendo una sustitución de texto simple, que está en línea con la definición de un preprocesador en Wikipedia .

Si este no es el uso adecuado de un preprocesador, ¿qué deberíamos llamar una simple sustitución de texto, que generalmente debe ocurrir antes de una compilación?


Para obtener el nombre del método ejecutado actualmente, puede ver el rastro de la pila:

public static string GetNameOfCurrentMethod() { // Skip 1 frame (this method call) var trace = new System.Diagnostics.StackTrace( 1 ); var frame = trace.GetFrame( 0 ); return frame.GetMethod().Name; }

Cuando está en un método de conjunto de propiedades, el nombre es set_Property.

Usando la misma técnica, también puede consultar el archivo fuente y la información de línea / columna.

Sin embargo, no hice una evaluación comparativa de esto, crear el objeto stacktrace una vez para cada conjunto de propiedades puede ser una operación que consume demasiado tiempo.


Sé que mucha gente piensa que el código corto es igual al código elegante, pero eso no es verdad.

El ejemplo que usted propone está perfectamente resuelto en código, como lo ha demostrado, ¿para qué necesita una directiva de preprocesador? No desea "preprocesar" su código, quiere que el compilador inserte código para usted en sus propiedades. Es un código común, pero ese no es el propósito del preprocesador.

Con tu ejemplo, ¿dónde pones el límite? Claramente eso satisface un patrón de observador y no hay duda de que será útil, pero hay muchas cosas que serían útiles que realmente se hacen porque el código proporciona flexibilidad donde el preprocesador no lo hace. Si intenta implementar patrones comunes mediante directivas de preprocesador, terminará con un preprocesador que debe ser tan poderoso como el idioma en sí. Si desea procesar su código de una manera diferente, use una directiva de preprocesador, pero si solo desea un fragmento de código, busque otro método, ya que el preprocesador no estaba destinado a hacer eso.


Creo que es posible que te falte una parte importante del problema al implementar INotifyPropertyChanged. Su consumidor necesita una forma de determinar el nombre de la propiedad. Por esta razón, debe tener sus nombres de propiedad definidos como constantes o cadenas de solo lectura estáticas, de esta manera el consumidor no tiene que ''adivinar'' los nombres de las propiedades. Si utilizó un preprocesador, ¿cómo sabría el consumidor cuál es el nombre de la cadena de la propiedad?

public static string MyPropertyPropertyName public string MyProperty { get { return _myProperty; } set { if (!String.Equals(value, _myProperty)) { _myProperty = value; NotifyPropertyChanged(MyPropertyPropertyName); } } } // in the consumer. private void MyPropertyChangedHandler(object sender, PropertyChangedEventArgs args) { switch (e.PropertyName) { case MyClass.MyPropertyPropertyName: // Handle property change. break; } }


Al menos para el escenario proporcionado, hay una solución más limpia y segura para el tipo que la construcción de un pre-procesador:

Usa genéricos Al igual que:

public static class ObjectExtensions { public static string PropertyName<TModel, TProperty>( this TModel @this, Expression<Func<TModel, TProperty>> expr ) { Type source = typeof(TModel); MemberExpression member = expr.Body as MemberExpression; if (member == null) throw new ArgumentException(String.Format( "Expression ''{0}'' refers to a method, not a property", expr.ToString( ))); PropertyInfo property = member.Member as PropertyInfo; if (property == null) throw new ArgumentException(String.Format( "Expression ''{0}'' refers to a field, not a property", expr.ToString( ))); if (source != property.ReflectedType || !source.IsSubclassOf(property.ReflectedType) || !property.ReflectedType.IsAssignableFrom(source)) throw new ArgumentException(String.Format( "Expression ''{0}'' refers to a property that is not a member of type ''{1}''.", expr.ToString( ), source)); return property.Name; } }

Esto puede ampliarse fácilmente para devolver un PropertyInfo lugar, lo que le permite obtener mucho más cosas que solo el nombre de la propiedad.

Como es un Extension method , puede usar este método en prácticamente todos los objetos.


Además, esto es tipo seguro.
No puedo enfatizar eso lo suficiente.

(Sé que es una pregunta antigua, pero encontré que carecía de una solución práctica).


Utilizando un preprocesador de estilo C ++, el código del OP podría reducirse a esta única línea:

OBSERVABLE_PROPERTY(string, MyProperty)

OBSERVABLE_PROPERTY se vería más o menos así:

#define OBSERVABLE_PROPERTY(propType, propName) / private propType _##propName; / public propType propName / { / get { return _##propName; } / set / { / if (value != _##propName) / { / _##propName = value; / NotifyPropertyChanged(#propName); / } / } / }

Si tiene 100 propiedades para tratar, eso es ~ 1,200 líneas de código contra ~ 100. ¿Cuál es más fácil de leer y entender? ¿Cuál es más fácil de escribir?

Con C #, suponiendo que corte y pegue para crear cada propiedad, eso equivale a 8 pastas por propiedad, 800 en total. Con la macro, sin pegar nada. ¿Cuál es más probable que contenga errores de codificación? ¿Cuál es más fácil de cambiar si tiene que agregar, por ejemplo, una bandera IsDirty?

Las macros no son tan útiles cuando es probable que haya variaciones personalizadas en un número significativo de casos.

Al igual que cualquier herramienta, las macros pueden ser objeto de abuso e incluso pueden ser peligrosas en las manos equivocadas. Para algunos programadores, este es un tema religioso, y los méritos de un enfoque sobre otro son irrelevantes; si eres tú, deberías evitar las macros. Para aquellos de nosotros que regularmente, hábilmente y de forma segura utilizamos herramientas extremadamente nítidas, las macros pueden ofrecer no solo una ganancia de productividad inmediata durante la codificación, sino también en etapas posteriores, durante la depuración y el mantenimiento.


El principal argumento en contra de la creación de un predecesor para C # es la integración en Visual Studio: se necesitarían muchos esfuerzos (si es posible) para obtener intellisense y la nueva compilación para que funcione sin problemas.

Las alternativas son usar un plugin de productividad de Visual Studio como ReSharper o CodeRush . Este último tiene, a mi leal saber y entender, un sistema de plantillas sin igual y viene con una excelente herramienta de refactorización .

Otra cosa que podría ser útil para resolver los tipos exactos de problemas a los que se refiere es un marco de AOP como PostSharp .
A continuación, puede usar atributos personalizados para agregar funcionalidad común.


Si está listo para deshacerse de C #, es posible que desee comprobar el lenguaje Boo que tiene un macro soporte increíblemente flexible a través de manipulaciones AST (Árbol de sintaxis abstracta). Realmente es genial si puedes abandonar el lenguaje C #.

Para obtener más información sobre Boo, consulte estas preguntas relacionadas: