Anyone know of any code that duplicates how the DebuggerDisplayAttribute
parses and gathers the resultant string?
I would like to create a custom attribute
Hopefully this code all fits... I made a non-reflection version of what you are trying to do, using Microsoft Roslyn and its C# Scripting ability to run the "code" in the attribute value as C# code.
To use this code, make a new C# project, and use NuGet to add a reference to Roslyn.
First the classes I'm using to test, just so you can see the attributes I tried.
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;
}
}
}
Now the tests (yes these all pass):
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));
}
}
}
Finally, the real goods:
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
Hopefully that helps you out. It was only about an hour and a half of work, so the unit testing isn't complete by any means, and I'm sure there are bugs in there somewhere, but it should be a solid start, if you are OK with the Roslyn approach.