2012-06-08 12 views
10

¿Alguien sabe de algún código que duplique cómo el DebuggerDisplayAttribute analiza y recopila la cadena resultante?¿Algún código que duplica cómo el DebuggerDisplayAttribute genera la cadena resultante?

Me gustaría crear un atributo personalizado que haga casi lo que se muestra. Similar a "Cuando se golpea un punto de interrupción ..." donde puede usar una variable dentro de llaves, como en "{variable}".

Ya manejo casos simples, como "{Name}", pero algo como "{Foo.Name}" requiere un código de reflexión adicional con el que necesito ayuda.

Básicamente, quiero analizar una cadena utilizando las reglas definidas en la documentación DebuggerDisplayAttribute. Actualmente, puedo analizar y resolver "Yo soy {GetName()}". Necesito ayuda con algo como "Foo's Name: {Foo.Name}"

+0

Está intentando cambiar el comportamiento de Visual Studio. Entonces, si bien podría crear fácilmente su propio atributo, necesitaría extender Visual Studio para reconocerlo e informarlo. Incluso entonces, no estoy seguro de si puedes cambiar el comportamiento hasta ese grado. Si es posible, este sería el lugar para comenzar: http://msdn.microsoft.com/en-us/library/bb161946.aspx – JDB

+0

@ Cyborgx37; No. Quiero duplicar la funcionalidad solamente. Para analizar la cadena y reunir los valores a través de la reflexión. – AMissico

+0

Lamentablemente, no creo que haya ninguna forma de hacerlo, consulte esta pregunta relacionada: http://stackoverflow.com/questions/2793965/does-there-exists-a-method-to-render-un-object-using -debuggerdisplayattribute Sería bueno ver esta funcionalidad expuesta en el Framework. Tal vez podrías solicitarlo conéctate, ¡si es así lo votaré! – Joe

Respuesta

2

Supongo que esto es para su propio uso (en equipo). Personalmente no lo he intentado, pero ¿has mirado las explicaciones sobre cómo personalizar el atributo DebuggerDisplay encontrado en here?

+0

Sé cómo funciona el atributo. Como mencioné, puedo manejar métodos o propiedades en el objeto principal. Son las propiedades y los métodos en las propiedades del objeto principal con los que necesito ayuda adicional. – AMissico

+0

No sugería nada más, pero pensé que no se habrían dado cuenta de que realmente (aparentemente) pueden alterar la forma en que DebuggerDisplayAttribute se expande editando autoexp.cs. No está explicando en su publicación exactamente qué funcionalidad está buscando. –

+0

Ah, pregunta actualizada. – AMissico

5

Esperemos que este código encaje ... Hice una versión no reflectante de lo que está tratando de hacer, usando Microsoft Roslyn y su capacidad de C# Scripting para ejecutar el "código" en el valor del atributo como código C#.

Para usar este código, cree un nuevo proyecto de C# y use NuGet para agregar una referencia a Roslyn.

Primero las clases que estoy usando para probar, solo para que pueda ver los atributos que probé.

using System.Diagnostics; 

namespace DebuggerDisplayStrings 
{ 
    [DebuggerDisplay("The Value Is {StringProp}.")] 
    public class SomeClass 
    { 
     public string StringProp { get; set; } 
    } 

    [DebuggerDisplay("The Value Is {Foo.StringProp}.")] 
    public class SomeClass2 
    { 
     public SomeClass Foo { get; set; } 
    } 

    [DebuggerDisplay("The Value Is {Seven() - 6}.")] 
    public class SomeClass3 
    { 
     public int Seven() 
     { 
      return 7; 
     } 
    } 
} 

Ahora las pruebas (sí todos estos pases):

using Microsoft.VisualStudio.TestTools.UnitTesting; 

namespace DebuggerDisplayStrings 
{ 
    [TestClass] 
    public class DebuggerDisplayReaderTests 
    { 
     [TestMethod] 
     public void CanReadStringProperty() 
     { 
      var target = new SomeClass {StringProp = "Foo"}; 
      var reader = new DebuggerDisplayReader(); 
      Assert.AreEqual("The Value Is Foo.", reader.Read(target)); 
     } 

     [TestMethod] 
     public void CanReadPropertyOfProperty() 
     { 
      var target = new SomeClass2 {Foo = new SomeClass {StringProp = "Foo"}}; 
      var reader = new DebuggerDisplayReader(); 
      Assert.AreEqual("The Value Is Foo.", reader.Read(target)); 
     } 

     [TestMethod] 
     public void CanReadMethodResultAndDoMath() 
     { 
      var target = new SomeClass3(); 
      var reader = new DebuggerDisplayReader(); 
      Assert.AreEqual("The Value Is 1.", reader.Read(target)); 
     } 
    } 
} 

bienes Por último, las reales:

using System.Collections.Generic; 
using System.Diagnostics; 
using System.Globalization; 
using System.Text.RegularExpressions; 
using Roslyn.Scripting.CSharp; 

namespace DebuggerDisplayStrings 
{ 
    public class DebuggerDisplayReader 
    { 
     // Get the fully evaluated string representation of the DebuggerDisplayAttribute's value. 
     public string Read(object target) 
     { 
      var debuggerDisplayFormat = GetDebuggerDisplayFormat(target); 
      if(string.IsNullOrWhiteSpace(debuggerDisplayFormat)) 
       return target.ToString(); 
      return EvaluateDebuggerDisplayFormat(debuggerDisplayFormat, target); 
     } 

     // Gets the string off the attribute on the target class, or returns null if attribute not found. 
     private static string GetDebuggerDisplayFormat(object target) 
     { 
      var attributes = target.GetType().GetCustomAttributes(typeof(DebuggerDisplayAttribute), false); 
      return attributes.Length > 0 ? ((DebuggerDisplayAttribute)attributes[0]).Value : null; 
     } 

     // Executes each bracketed portion of the format string using Roslyn, 
     // and puts the resulting value back into the final output string. 
     private string EvaluateDebuggerDisplayFormat(string format, object target) 
     { 
      var scriptingEngine = new ScriptEngine(new[] { GetType().Assembly }); 
      var formatInfo = ExtractFormatInfoFromFormatString(format); 
      var replacements = new List<object>(formatInfo.FormatReplacements.Length); 
      foreach (var codePart in formatInfo.FormatReplacements) 
      { 
       var result = scriptingEngine.Execute(codePart, target); 
       replacements.Add((result ?? "").ToString()); 
      } 
      return string.Format(formatInfo.FormatString, replacements.ToArray()); 
     } 

     // Parse the format string from the attribute into its bracketed parts. 
     // Prepares the string for string.Format() replacement. 
     private static DebuggerDisplayFormatInfo ExtractFormatInfoFromFormatString(string format) 
     { 
      var result = new DebuggerDisplayFormatInfo(); 
      var regex = new Regex(@"\{(.*)\}"); 
      var matches = regex.Matches(format); 
      result.FormatReplacements = new string[matches.Count]; 
      for (var i = matches.Count - 1; i >= 0; i--) 
      { 
       var match = matches[i]; 
       result.FormatReplacements[i] = match.Groups[1].Value; 
       format = format.Remove(match.Index + 1, match.Length - 2).Insert(match.Index+1, i.ToString(CultureInfo.InvariantCulture)); 
      } 
      result.FormatString = format; 
      return result; 
     } 
    } 

    internal class DebuggerDisplayFormatInfo 
    { 
     public string FormatString { get; set; } 
     public string[] FormatReplacements { get; set; } 
    } 
} 

suerte que le ayuda a cabo. Solo fue una hora y media de trabajo, por lo que las pruebas unitarias no están completas de ninguna manera, y estoy seguro de que hay errores en alguna parte, pero debería ser un comienzo sólido, si estás de acuerdo con el Roslyn se acerca.

+0

http://blogs.msdn.com/b/visualstudio/archive/2011/10/19/introducing-the-microsoft-roslyn-ctp.aspx – AMissico

+0

http://en.wikipedia.org/wiki/Microsoft_Roslyn – AMissico

+0

http : //www.microsoft.com/en-us/download/details.aspx? id = 27746 – AMissico

Cuestiones relacionadas