In bed with Roslyn

Microsoft have been talking about their “C# compiler as a service” project, aka Roslyn, for a couple of years now, and yesterday the C# team finally released a preview version. Being a bit of a language geek, I was interested to see if I could use Roslyn not only to perform analysis and refactorings but to extend the C# language itself. The reason for wanting to do this is to replace repetitive and error-prone boilerplate code with terser, more expressive code — quicker to write, easier to read and no chance for human error to muck it up.

The motivating example

If you’ve written much WPF or Silverlight code, you’ve undoubtedly written a bunch of dependency properties. Because C# has no built-in support for DPs, this means you have to write a bunch of code spread across several members, something like this:

public static readonly DependencyProperty WidgetudeProperty =
  DependencyProperty.Register("Widgetude", typeof(int), typeof(Widget),
    new FrameworkPropertyMetadata(OnWidgetudeChanged));
 
public int Widgetude
{
  get { return (int)GetValue(WidgetudeProperty); }
  set { SetValue(WidgetudeProperty, value); }
}
 
private static void OnWidgetudeChanged(DependencyObject d, DependencyPropertyChangedEventArgs e)
{
  // on-change logic here
}

This is not only boring to write, it is hard to read because the code is cluttered with boilerplate, and it is prone to error. If I had a dollar for everybody who put business logic in the CLR property setter and then wondered why it wasn’t being run when a binding updated… well, I’d have $3.00. And it’s awful to maintain. For example, if I change the property to a double, I have to update the type in three places. If I change the property name, I have to update it in seven!

Wouldn’t it be sweet if C# had language support for DPs, so you could write:

[dependency] public int Widgetude
{
  change { /* on-change logic here */ }
}

and the C# compiler would treat it as though you’d written all the boilerplate? Of course, that’s impossible because even if you used something like PostSharp to do a rewriting at the IL level, there’s no way to represent a “property change” element in IL — it’s simply not legal C#.

Enter Roslyn.

Roslyn is forgiving

Roslyn is about building tooling for source code, and one of the nasty facts about source code is that it is in an invalid state more often than not. For example, you may be halfway through writing an ‘if’ statement and haven’t done the closing brace yet. Or you’ve written a call to a method but you haven’t written that method yet. Or you’ve misspelt ‘get’ as ‘grt’ in a property accessor.

A compiler won’t stand for this nonsense. Roslyn, at least at the parsing stage, is much more forgiving. You can write any old gibberish and Roslyn will do its best to organise it into a nice syntax tree, even if that syntax tree is uncompilable.

This forgiving nature means we can sneak our own illegal constructs into Roslyn, and use the Roslyn APIs to transform them into legal constructs. It’s a risky business, because Roslyn will try to interpret whatever we write as valid C#, so we probably shouldn’t be relying on any particular interpretation of invalid C#, but what the heck. If you’re going to have a sweet, forgiving nature, you must expect people to take advantage of it.

Reading code in with Roslyn

Reading code in with Roslyn is a snap — just call SyntaxTree.ParseCompilationUnit.

SyntaxTree tree = SyntaxTree.ParseCompilationUnit(sourceCode);
var root = (CompilationUnitSyntax)(tree.Root);

A SyntaxTree is an abstract representation of the source code — warts and all. It includes everything: good code, bad code, whitespace, rude comments about the client, rude comments about the client’s mother, the lot. This is all represented as a tree — at the top is a root node containing usings, members, trivia (e.g. whitespace and comments), etc., then each member (e.g. a namespace) contains sub-members (e.g. the classes in that namespace), and so on down to individual statements and expressions.

If we give Roslyn our made-up dependency property syntax (suitably enclosed in a class of course), it cheerfully parses it as a property (represented as a PropertyDeclarationSyntax object). And the change { } fragment looks enough like a get or set accessor that the Roslyn parser actually accepts it as an accessor, noting only with clinical disinterest that it wasn’t recognised as the get or set accessor.

Of course, the compiler stage of Roslyn would pitch a fit if it saw an unrecognised accessor. But we’re not going to ask the compiler stage its opinion. We’re going to take what the parser produces, and turn it into something of our own.

Rewriting code with Roslyn

Roslyn syntax trees are immutable. This is computer science jargon for “good,” but it does mean we can’t just party on the SyntaxTree object. Instead we have to transform it to produce a new SyntaxTree object. Roslyn provides a bunch of helper methods for updating, extending and pruning syntax trees, but for our purposes we’re going to use a gadget called SyntaxRewriter. SyntaxRewriter lets you traverse an entire syntax tree making whatever changes you want (though again, remember that ‘making changes’ doesn’t actually modify an existing object, it produces a new one).

Our syntax rewriter is going to rewrite our property to be a CLR dependency property wrapper, and it’s also going to inject a DependencyProperty field and an optional change callback into the enclosing class. That means we’re interested in two kinds of syntax tree node. First, we’re interested in property declarations, because (a) we need to rewrite them and (b) we need to generate fields and callback methods from them. Second, we’re interested in class declarations, because we need to poke those fields and callback methods into the containing class.

This observation makes it easy to stub out our rewriter.

public class DPRewriter : SyntaxRewriter
{
  private readonly List<FieldDeclarationSyntax> _fields = new List<FieldDeclarationSyntax>();
  private readonly List<MethodDeclarationSyntax> _methods = new List<MethodDeclarationSyntax>();
 
  protected override SyntaxNode VisitPropertyDeclaration(PropertyDeclarationSyntax node)
  {
    // TBA
  }
 
  protected override SyntaxNode VisitClassDeclaration(ClassDeclarationSyntax node)
  {
    // TBA
  }
}

The fields and methods collections are going to hold the dependency property fields and the change callback methods that we generate during property visits, so that we can later inject them into the class.

I’ve also been lazy and chosen an attribute-like syntax for marking a property as a dependency property. This makes the first stage of our property handler pretty easy too:

protected override SyntaxNode VisitPropertyDeclaration(PropertyDeclarationSyntax node)
{
  bool isDP = node.Attributes
                  .SelectMany(a => a.ChildNodes().OfType<AttributeSyntax>())
                  .Any(a => a.Name.PlainName == "dependency");
  if (!isDP)
  {
    return base.VisitPropertyDeclaration(node);
  }
 
  // real work TBA
}

Unfortunately, this is pretty much the end of the easy bit. Now we need to dive in and actually do our rewriting.

Rewriting the dependency property syntax

You probably want to see some code here, but you see, the other thing about Roslyn is, Roslyn is verbose. Really, really, verbose. I mean, it makes CodeDom look terse. It’s not really Roslyn’s fault — you’re manipulating syntax trees which are trying to provide full flexibility and fidelity to source code, so there’s bound to be a lot of detail and depth — but it does make it a pain in the backside to post any useful chunk of Roslyn code.

Consider yourself warned.

Our rewrite code needs to do three things:

  • Add a field of type DependencyProperty to the fields list
  • Replace the property with the DP implementation
  • If the property had a change ‘accessor,’ wrap the body of that accessor up in a method and add that to the methods list

To build new Roslyn objects, we use the Syntax.* factory methods. Each of these takes 84 parameters, most of which are optional, so if you don’t like C# named arguments, look away now. Here, for example, is how to build the field. (BIG CAVEAT: I’ve only been playing with Roslyn for a few hours. I’m pretty sure I’m using the wrong idioms for some things, and that there are easier ways to do others. Don’t take this as a best practice guide.)

// Desired output:
// DependencyProperty XxxProperty = DependencyProperty.Register("Xxx",
//   typeof(PropType), typeof(OwnerType),
//   new FrameworkPropertyMetadata(OnXxxChanged);
 
// Set up some useful elements
var changeCallback = node.AccessorList.Accessors.FirstOrDefault(a => a.Kind == SyntaxKind.UnknownAccessorDeclaration && a.Keyword.ValueText == "change");
bool hasChangeCallback = changeCallback != null;
 
var dpType = Syntax.ParseTypeName("System.Windows.DependencyProperty");
 
var ownerType = Syntax.ParseTypeName(node.FirstAncestorOrSelf<ClassDeclarationSyntax>().Identifier.ValueText);
string propName = node.Identifier.ValueText;
ExpressionSyntax propNameExpr = Syntax.LiteralExpression(SyntaxKind.StringLiteralExpression, Syntax.Literal(text: '"' + propName + '"', value: propName));
ExpressionSyntax typeofPropType = Syntax.TypeOfExpression(argumentList: Syntax.ArgumentList(arguments: Syntax.SeparatedList(Syntax.Argument(expression: node.Type))));
ExpressionSyntax typeofOwnerType = Syntax.TypeOfExpression(argumentList: Syntax.ArgumentList(arguments: Syntax.SeparatedList(Syntax.Argument(expression: ownerType))));
 
// Build the arguments to the Register call
var registerArgs = new List<ArgumentSyntax> {
  Syntax.Argument(expression: propNameExpr),
  Syntax.Argument(expression: typeofPropType),
  Syntax.Argument(expression: typeofOwnerType)
};
 
if (hasChangeCallback)
{
  ExpressionSyntax changeMethod = Syntax.ParseName("On" + propName + "Changed");
  ExpressionSyntax fpm = Syntax.ObjectCreationExpression(
    type: Syntax.ParseTypeName("System.Windows.FrameworkPropertyMetadata"),
    argumentListOpt: Syntax.ArgumentList(
      arguments: Syntax.SeparatedList(Syntax.Argument(expression: changeMethod))
    )
  );
  registerArgs.Add(Syntax.Argument(expression: fpm));
}
 
var argSeparators = Enumerable.Repeat(Syntax.Token(SyntaxKind.CommaToken), registerArgs.Count - 1).ToList();
 
// Build the call to the Register method
ExpressionSyntax dpexpr = Syntax.InvocationExpression(
  expression: Syntax.ParseName("System.Windows.DependencyProperty.Register"),
  argumentList: Syntax.ArgumentList(
    arguments: Syntax.SeparatedList(registerArgs, argSeparators)
  )
);
 
// Build the field variable
string fieldName = propName + "Property";
VariableDeclaratorSyntax declarator = Syntax.VariableDeclarator(
  identifier: Syntax.Identifier(fieldName),
  initializerOpt: Syntax.EqualsValueClause(
    value: dpexpr
  )
);
 
// Build the field declaration
FieldDeclarationSyntax newField = Syntax.FieldDeclaration(
  modifiers: Syntax.TokenList(Syntax.Token(SyntaxKind.PublicKeyword), Syntax.Token(SyntaxKind.StaticKeyword)),
  declaration: Syntax.VariableDeclaration(
    type: dpType,
    variables: Syntax.SeparatedList(declarator)));
 
// Store it to add to the class later
_fields.Add(newField);

Don’t panic at this. I know it’s long by bloggy standards (about 50 lines), and that it appears very dense. But a lot of the density is down to the named arguments and the deep nesting of Roslyn trees, so if you walk through it methodically, it’s not that difficult, just lengthy. Here’s what happens:

  • We build up ExpressionSyntax objects representing the arguments to the DependencyProperty.Register call: the property name literal string, the typeof() expressions for the property type and the owner type, and optionally a “new FrameworkPropertyMetadata” (ObjectCreationExpression) with a reference to the callback method name
  • We build a call to the DependencyProperty.Register method, passing the arguments we built in the last step. Roslyn asks us to wrap the arguments in an ArgumentList, which incurs some tedious messing around with separators. Roslyn may be forgiving on the read side, but it’s pedantic as all hell on the write side!
  • We build the field declaration itself. This comes in two bits. First we create a VariableDeclaratorSyntax representing the field variable and the initialiser for that variable. The initialiser is just the DependencyProperty.Register call we built in the previous step. Then we wrap this up with a “public” visibility modifier and a data type into a FieldDeclarationSyntax.
  • Job done — stick the FieldDeclarationSyntax in the collection for later processing.

After this epic, the other two bits are relatively concise. Here’s the code that generates the CLR wrapper property:

// Desired output:
// public PropType Xxx
// {
//   get { return (PropType)GetValue(XxxProperty); }
//   set { SetValue(XxxPropety, value); }
// }
ExpressionSyntax getval = Syntax.ParseExpression("GetValue(" + fieldName + ")");
ExpressionSyntax casty = Syntax.CastExpression(type: node.Type, expression: getval);
StatementSyntax getter = Syntax.ReturnStatement(expressionOpt: casty);
 
StatementSyntax setter = Syntax.ParseStatement("SetValue(" + fieldName + ");");
 
PropertyDeclarationSyntax newProperty = Syntax.PropertyDeclaration(
  modifiers: Syntax.TokenList(Syntax.Token(SyntaxKind.PublicKeyword)),
  type: node.Type,
  identifier: node.Identifier,
  accessorList: Syntax.AccessorList(
    accessors: Syntax.List(
      Syntax.AccessorDeclaration(
        kind: SyntaxKind.GetAccessorDeclaration,
        bodyOpt: Syntax.Block(
          statements: Syntax.List(
            getter
          )
        )
      ),
      Syntax.AccessorDeclaration(
        kind: SyntaxKind.SetAccessorDeclaration,
        bodyOpt: Syntax.Block(
          statements: Syntax.List(
            setter
          )
        )
      )
    )
  ));

Again, there’s lots of fiddly wrapper classes and lots of nesting, but the core concepts aren’t too hard. We build a GetValue call, apply a cast to it, and stick a return statement on it, and that’s our getter body. For our setter body, we build a SetValue call. Notice that Roslyn lets us build our code as C# source using string operations via the Parse methods, so we don’t have to do everything with trees. Then we bundle the getter and setter bodies up into accessors and stuff them into a property declaration.

Finally comes the on-changed callback. This is relatively easy because all we need to do is create a method, and transplant whatever the user wrote inside the change { } pseudo-accessor into that method.

// Desired output:
// private static void OnXxxChanged(DependencyObject d, DependencyPropertyChangedEventArgs e)
// {
//   /* body */
// }
 
// Reminder
var changeCallback = node.AccessorList.Accessors.FirstOrDefault(a => a.Kind == SyntaxKind.UnknownAccessorDeclaration && a.Keyword.ValueText == "change");
bool hasChangeCallback = changeCallback != null;
 
if (hasChangeCallback)
{
  // Specify the method parameters (always d and e)
  List<ParameterSyntax> parameterList = new List<ParameterSyntax>
  {
    Syntax.Parameter(identifier: Syntax.Identifier("d"), typeOpt: Syntax.ParseTypeName("System.Windows.DependencyObject")),
    Syntax.Parameter(identifier: Syntax.Identifier("e"), typeOpt: Syntax.ParseTypeName("System.Windows.DependencyPropertyChangedEventArgs")),
  };
  var paramSeparators = Enumerable.Repeat(Syntax.Token(SyntaxKind.CommaToken), parameterList.Count - 1).ToList();  // must be an easier way
  ParameterListSyntax parameters = Syntax.ParameterList(
    parameters: Syntax.SeparatedList(parameterList, paramSeparators)
  );
 
  // Build the method
  MethodDeclarationSyntax changeMethod = Syntax.MethodDeclaration(
    modifiers: Syntax.TokenList(Syntax.Token(SyntaxKind.PrivateKeyword), Syntax.Token(SyntaxKind.StaticKeyword)),
    identifier: Syntax.Identifier("On" + propName + "Changed"),
    returnType: Syntax.PredefinedType(Syntax.Token(SyntaxKind.VoidKeyword)),
    parameterList: parameters,
    bodyOpt: changeCallback.BodyOpt
  );
  _methods.Add(changeMethod);
}

A lot of the noise here comes from building up the parameter list; once we get into building the method, you can almost read off what you need to do from the desired output syntax. public static, those are the modifiers; OnXxxChanged, that’s the identifier; void, that’s the return type; and the method body, well hey, Roslyn has already parsed that into body of the change pseudo-accessor, so we just pass a reference to that!

Taken together, this probably looks intimidating. Brave heart! It’s not difficult, just long and fiddly. Everything we’re doing here maps very exactly to the C# syntax in the “desired output” sections. If you feel yourself getting lost, map it back to those.

One final thing: our property rewriter needs to return the rewritten property.

return newProperty;

Back on familiar ground at last!

Rewriting the class

Okay, so we rewrote the property by returning it from VisitPropertyDeclaration, but how are we going to get those fields and methods we’ve been collecting emitted into the generated code? Well, fields and methods live at class level, so for this we need to rewrite the class. Fortunately, we only need to do a very simple rewrite, just jamming in a few extra members. Here’s the code:

protected override SyntaxNode VisitClassDeclaration(ClassDeclarationSyntax node)
{
  var newTypeDeclaration = (TypeDeclarationSyntax)base.VisitClassDeclaration(node);
 
  if (_fields.Count > 0 || _methods.Count > 0)
  {
    var members = new List<MemberDeclarationSyntax>(newTypeDeclaration.Members);
    members.InsertRange(0, _methods);
    members.InsertRange(0, _fields);
 
    return ((ClassDeclarationSyntax)newTypeDeclaration).Update(
        newTypeDeclaration.Attributes,
        newTypeDeclaration.Modifiers,
        newTypeDeclaration.Keyword,
        newTypeDeclaration.Identifier,
        newTypeDeclaration.TypeParameterListOpt,
        newTypeDeclaration.BaseListOpt,
        newTypeDeclaration.ConstraintClauses,
        newTypeDeclaration.OpenBraceToken,
        Syntax.List(members.AsEnumerable()),
        newTypeDeclaration.CloseBraceToken,
        newTypeDeclaration.SemicolonTokenOpt);
  }
 
  return newTypeDeclaration;
}

What’s the deal? The first thing we have to do is call the base class implementation, which goes off to all the elements within the class and gives them the chance to rewrite themselves. This is essential, because otherwise our property rewriter would never get called. And it’s also essential to do it first, so that the fields and methods that are by-products of the property rewriting are ready when we need them.

Then we take a copy of the existing members of the class, add in the fields and methods that came out of the property rewriting process, and use the Update helper method to create a copy of the class with the extended member collection. As before, it looks scarier than it is — most of the Update call is boilerplate copying, there’s just quite a bit of it!

Let’s rewrite

Finally, we need to call the rewriter to transform our illegal pseudo-C# into legal C#.

SyntaxTree tree = SyntaxTree.ParseCompilationUnit(sourceCode);
var root = (CompilationUnitSyntax)(tree.Root);
 
var rewriter = new DPRewriter();
var newRoot = (CompilationUnitSyntax)(rewriter.Visit(root));
newRoot = newRoot.Format();
Console.WriteLine(newRoot.ToString());

Just new up a rewriter, pass it the node you want to rewrite — in our case we’ll rewrite the whole source by passing the root — and call ToString() on the result to get the transformed code. Easy!

Here’s the results (slightly reformatted for readability):

// Input:
public class Widget : DependencyObject
{
  [dependency] public int Widgetude
  {
    change { Console.WriteLine("DP change: " + e + " on " + d); }
  }
}
 
// Output:
public class Widget : DependencyObject
{
  public static System.Windows.DependencyProperty WidgetudeProperty =
    System.Windows.DependencyProperty.Register("Widgetude",
      typeof (int), typeof (Widget),
        new System.Windows.FrameworkPropertyMetadata(OnWidgetudeChanged));
 
  private static void OnWidgetudeChanged(System.Windows.DependencyObject d, System.Windows.DependencyPropertyChangedEventArgs e)
  {
    Console.WriteLine("DP change: " + e + " on " + d);
  }
 
  public int Widgetude
  {
    get { return (int)GetValue(WidgetudeProperty); }
    set { SetValue(WidgetudeProperty); }
  }
}

Sweet!

Take it for a spin

The possibilities for Roslyn make my head spin. It’s going to lower the barrier to entry for building code analysis and refactoring tools, which I reckon is going to lead to a huge flowering of new and cool products in this sector. But it also opens up some exciting new possibilities for code generation — from the legitimate, like aspect weaving on source code, to the slightly naughty, like syntax extensions.

You can get the Roslyn preview for VS2010 SP1 here. It has a bunch of great samples and walkthroughs which are well worth checking out. My code is about a million times uglier, and is I’m sure riddled with unnecessary kludges and workarounds, but if you want to have a play with it, here it is.

Enjoy, and go crazy!

Enjoyed this post? Please upvote on Hacker News or Reddit — thanks!

Tagged as Visual Studio

18 Responses to “In bed with Roslyn”

  • In bed with Roslyn – extending C# syntax…

    Thank you for submitting this cool story – Trackback from DotNetShoutout…

  • Very well written!
    We’re still deciding how to improve the Update method design to not have to pass all the non-changing properties. Also (as @Pilchie mentions) in Roslyn you can cheat and use Syntax.ParseExpression, Syntax.ParseParameterList etc. to ease the work a little bit.

  • Thanks for the feedback, Kirill. Yes, ParseParameterList would have tightened things up quite a bit! I’m not going to try to rework the code at this stage, but hopefully people will find your comment or Kevin’s tweet and follow your tips instead of my fumblings.

    I’ll drop you a line with some more detailed feedback, albeit still very preliminary!

  • [...] In bed with Roslyn – Ivan Towlson takes the wraps off the Roslyn CTP and starts digging down into how it can be used o enhance your productivity by manipulating code at compile time to easily implement tiresome features like dependency properties. [...]

  • I wouldn’t be supprised to see Roslyn explicitly supporting syntax extensions once it’s a little more developed – surely the rewriting being done for the async/await keywords would be a core scenario they’d want to support.

  • [...] In bed with Roslyn (Ivan Towlson) [...]

  • Indeed, well written and interesting article.

    “If you’re going to have a sweet, forgiving nature, you must expect people to take advantage of it.” Priceless.

    I know the point of your article isn’t to actually revolutionize the way people write dependency properties–and a reasonably good, type-safe property registration can be invented with some generics/lambda expression magic–but I’m curious about how you actually foresee using re-writing code like this?

    You’d lose some of the maintainability benefits of your invented syntax, but it seems possible to turn this into a development-time refactoring operation.

    In fact, if the refactoring were bi-directional (’cause hey, you can create invalid syntax trees easily enough!), you could have a workflow something like: 1) author code using preferred syntax; 2) transform to valid C#; 3) time passes… 4) transform back to preferred syntax to edit; 5) transform back into valid C#.

    The bidirectionality of the transformation is what would make this more powerful than a regular old snippet.

  • [...] In bed with RoslynFuture directions for C# and Visual BasicFuture directions for C# and Visual Basic… [...]

  • Hi Jacob,

    It’s absolutely possible to run a Roslyn transformation as a development-time refactoring or ‘quick fix.’ Indeed that’s the scenario many of the samples envisage (for example, there is a sample where you can click a plain property in Visual Studio and implement INotifyPropertyChanged on it). This is probably the most realistic scenario given Microsoft’s limited goals for Roslyn, as they’re not exposing the compiler pipeline, but if you wanted to automate the transform you could do it in a pre-build step or using a custom tool or something like that.

    Bidirectional transforms are an interesting idea. As you say, that would make a transform like this a super-snippet, where you could temporarily go back to the snippet form, edit it there, and then return to the full legal form. I have to admit my interest is in working at the higher level of abstraction, not manually having to switch in and out. For example, consider readability: I’d like to be able to read code at the ‘DP’ level, not have to reassemble ‘okay, these three chunks of raw C# together form a DP’ in my mind. Like when you build a LINQ to SQL or LightSpeed model in a visual designer, you’re working at a ‘boxes and arrows’ level of abstraction instead of down at the code level of abstraction (you know there is code behind the abstraction, but you don’t have to spell out or look at every detail of that code). That’s all I’m thinking about, except instead of being restricted to graphical abstractions I’m talking about textual abstractions.

    That said, I think your idea of super-snippets is much more realistic. Microsoft have been pretty clear that Roslyn is about tooling rather than language extensibility, and smarter snippets fall very neatly into that category. You can also envisage tooling to maintain such code e.g. watching for edits to code that looks like the DP pattern and going “Oi! Don’t put logic in the setter!” or “Do you want to change the name/type throughout the pattern?”

    Hope that makes sense — appreciate any thoughts!

  • Yup, I’m with you.

    I do like the idea of adding squiggly underlines for logic in a DependencyProperty’s set accessor.

    But yeah, the idea I really want to explore is low friction applications of transformations like the one you propose. Perhaps I need to do some more reading about custom tools…

  • What? Roslyn is not about language extensibility? What’s the point of it then?…
    My dream was always to kind of attach a roslyn project to my solution, to introduce specific new keywords and maybe some form of dsl, that completely vanishes away the applications specific boilerplate code replaced with cool expressive syntax, and that efficiently! I’d doupt that your ‘refactoring’ would/could be supported by the syntax highlighter? Isn’t there an api do kinda define new keywords or something?

  • Hi Phil,

    Yes, that’s what I want too! I don’t see any reason an extended language couldn’t be suitably highlighted by an extended language service: effectively you’d be building a new “extensible C#” or “C# with macros” language on top of the existing C# language, and preprocessing that into baseline C#, so you’d just need to build a language service for that new language alongside the preprocessor.

    There isn’t an API to define new keywords. At the moment defining new keywords or operators is very ad hoc: you need to figure out how Roslyn would parse your construct, look out for that pattern and perform syntax rewriting based on that. I’ve got some ideas on how they could change the API to make this more uniform and I’ll suggest them to the Roslyn folks but they need to be sure that any changes don’t have a negative impact on their core scenarios.

  • Now Roslyn really looks fun to use! The only problem is that it’s still CTP and you may need to rewrite it later on…but I guess it shouldn’t be too much work.

    Today I use T4 to handle that kind of stuffs. I would write eg “public int Widgetude;” and any other properties in MyCSharpClass.t4, and my T4 program would create a MyCSharpClass.generated.cs file, containing “public partial class MyCSharpClass {}” and all the automatically generated DPs. If your generated code needs to spread over several files, it’s still possible but you’ll need to specify somewhere the path to them.

  • John, that’s absolutely right. In fact we should be *expecting* breaking changes — the Roslyn folks have been clear that this release is to get feedback on the API.

    The T4 solution does work nicely for cases where the input is easy to parse and the output can be mixed in at class level, which is certainly the case for dependency properties. Where T4 can’t help is if you want to introduce constructs that are usable inside a method. For example, suppose you wanted to implement an “unless” operator:

    unless (condition) { doStuff(); }

    You couldn’t do that in T4 unless you put every method that used “unless” into the T4 partial of the class. Even if you were willing to accept that, your T4 program would have to be smart enough to parse its source to some degree (you couldn’t just do a global replace of “unless (” with “if (!”), which would make the T4 program quite a bit more complicated.

    Roslyn helps with the first by allowing you to splice into method bodies as well as at the class level, and with the second by giving you a best guess parse as a starting point. (The latter, I should admit, is often not ideal when you’re playing with deliberately invalid C#, but it’s still better than nothing!)

    That said, you are absolutely right that the DP example can be done today with T4, and probably more easily than the Roslyn solution — at the expense of course of having to define the DPs in a separate file.

  • If you’d make the input:

    [dependency] public int Widgetude(DependencyObject d, DependencyPropertyChangedEventArgs e)
    {
    Console.WriteLine(“DP change: ” + e + ” on ” + d); }
    }

    Then it will be not only syntacticly correct, but even semantically. This will allow other refactoring and code analysis tools (including IntelliSense) to analyze your source code without shouting at you. Clearly you can produce the same output out of this input.

  • But that’s *not* semantically correct. Widgetude is a *property*, not an event handler. There is an event handler associated with it, but it is not itself a method, and it shouldn’t pretend to be one.

    And code analysis tools won’t be able to analyse your source code without shouting at you any more than in my original example, because elsewhere in the code you’ll be addressing the DP as a property, e.g. writing “myControl.Widgetude = 123;” If the tools think Widgetude is a method, they’ll barf on this. You’ve traded illegal syntax at the declaration for illegal usage everywhere else in the program. Plus you’ll get shrieking even at the declaration site, because you’ve declared the Widgetude ‘method’ as returning int, but the method body doesn’t return a value.

    But you’re right that the specific case of ‘DP with change callback’ could technically be handled by writing a method and transforming that. However, what about the case of ‘DP with no change callback,’ or ‘DP with change, validate and coerce callbacks’? You can’t represent those as methods: in the first case, you specifically don’t *want* a method, and in the second, you need several bodies.

  • This is pretty neat, but I see two problems in all of this. First, the amount of code to do a rewriting is a bit too much. I’ve done similar things with Boo (a Python-like CLR language), and it took a lot less code. I think that what people have to understand is that, in most cases, metaprogramming is better as a _textual_ rewrite instead of an AST transform. Take a look at D’s `mixin` keyword – that’s a lot more sensible for something like 99% of use cases.

    The second problem is that once you extend the language, any kind of syntax highlighting, code completion, etc. simply breaks down. And the way in which postprocessing is defined does not, at least to me, offer any possibility for the IDE or any tool like ReSharper to deterministically infer what’s meant at this location. That, in turn, implies that you cannot do analysis, refactoring, etc. on code that’s manipulated this way.

  • Yes, Boo is certainly far easier to extend than C#!

    However, I’d take issue with the claim that “in most cases, metaprogramming is better as a textual rewrite.” You can certainly make that case on the emit side (though AST transformation has its advantages, particularly for non-local transforms), but not on the read side: otherwise you end up having to implement a parser for the language anyway in order to recognise the pattern you want to transform.

    I haven’t looked at the code completion, highlighting, etc. side but I don’t think this is as prohibitive as you do. Because the IDE is also using Roslyn, the extension transforms can run in the IDE and thereby participate in highlighting, analysis, etc. Don’t know whether those hooks exist yet though or how hard they are to use!

  • Leave a Reply

Archives

Join our mailer

You should join our newsletter! Sent monthly:

Back to Top