The use of this is a little specialised, mainly aimed at code that manipulates and analyses sourcecode.
Basic concept is to allow the coder to write the code they what to make as a Code Template and the compiler generates to code Roslyn's AST needed to make it.
How these would be expressed in code is up for discussion, as a base to start from I'm use the following. The type Expr<T> denotes an expression that evaluates to a T, a statement would be an Expr<Void>..
Codeplex Thread: Template Functions & Template Masks
Template Function
template IFExpression _IF_ ( Expr<Bool> cond, Expr truecode, Expr falsecode )
{
if( /{ cond : Expr<Bool> }/ )
{
/{ truecode }/
}
else
{
/{ falsecode }/
}
}
Template Mask
This example will use template functions and template mask to replace If - Else structure to Inline if structure.
This template mask matches against assignments.
template mask simple_assignment() : template_mask
{
|{ /{ target }/ = /{ source }/ ; |}
}
This template generates an assignment that utilises and inline if expression. cond ? true : false
template assignment_viaCondition_ <T>( target : __ , cond : Expr<Bool> ,
valueA : Expr<T> , valueB : Expr<T> ) : CS.Expr
{
|{ var /{ target }/ = (/{ cond }/ ) ? ( /{ valueA }/ ) : ( /{ valueB }/ ) ; |}
}
The following template mask matches against the if else construct.
template mask _IfElse_() : template_mask
{
|{ if( /{ cond }/
{
/{ on_true }/
}
else
{
/{ on_false }/
}
}|
}
So let's utilise the previously construct templates and template masks.
AnalyseNode ( node : SyntaxNode )
{
var mn = template.Match( node , _IfElse_ )
if( !mn.Valid ) return
var part_0 = template.Match( mn["on_true"], simple_assignment )
var part_1 = template.Match( mn["on_false"], simple_assignment )
if( part_0.Valid && part_1.Valid )
{
if( part_0["target"] == part_1["target"] )
{
node.ReplaceWith( assignment_viaCondition_( part_0["target"] , mn["cond"] , part_0["source"], part_1["source"] );
}
}
}
Some of the functionality of Template Function can be implemented with the use of String Interpolation. Eg
SyntaxFactory.ParseExpression(
$"if( \{parameter.Identifier} == null ) { throw newArgumentNullException( nameof( \{parameter.IdentifieIdentifier.Text} )); }" );
Templates are better way to speed up coding than code snippets, because templates can be edit once and reflects in all usages, and you can have different templates in different project types.
Templates can be defined in namespaces and classes, like delegate, a demo for define template as follows:
namespace MyTemplates
{
public template DependencyObject DefineProperty(raw type, raw name)
{
public DependencyProperty ${name}Property { get; } = DependencyProperty.Register("${name}", typeof(${type}), typeof(${scope.ClassName}), new PropertyMetadata(default(${type}));
public ${type} ${name}
{
get { return (${type})GetValue(${name}Property); }
set { SetValue(${name}Property, value); }
}
}
}
public is the access modifier of this template, so there will not be a whole world of templates.
template keyword indicates it's a template.
DependencyObject is the scope modifier to indicates will this template can be used, it must be one of the following values:
class keyword: Same as using object type, this template can be placed in any classes.struct keyword: This template can be placed in any structs.namespace keyword: This template can be placed in a namespace. This template can contain one or more classes, structs, enumerations, or even other templates.method keyword: This template can only be placed in method body, it should contain some useful logic for methods.DefineProperty is the name of the template. Uses ${TemplateName(args)} to use this template in a valid scope.
raw type, raw name is the argument list of the template, raw is the type, it can be one of the following values:
raw keyword: Given as string, but when compiling, the quote marks are now included.${DefineProperty("bool", "IsEnabled")}, public ${type} ${name} will be compiled to public bool IsEnabled.In template body, uses ${argumentName} to reference an argument, when compiling, a normal type argument will be directly replaced, for example
template method Print(object value) { System.Console.Write(${value}); }
void Main() { ${Print(1)} }
compiles to
void Main() { System.Console.Write((object)1); }
Another example for method scope template:
template method TestNotNull(object parameter) { if (${parameter} == null) throw new ArgumentNullException(nameof(${parameter})); }
void SomeMethod(string stringArg) { ${TestNotNull(stringArg)} }
compiles to
void SomeMethod(string stringArg) { if ((object)stringArg == null) throw new ArgumentNullException(nameof(stringArg)); }
And in template body, a special object, scope, contains information of the current scope, for example when used in a class, scope.ClassName contains the name of the containing class.
Templates can be compiled as a special type into binaries, other project can reference this library to use these templates.
If I understand this, it is a proposal for a macro system.
Closing, as we do not want to add a macro system to C# of VB.net.
@gafter It not entirely about macros. It's also about design-time code template (akin to VS snippets). It would allow "snippets" to be within a specific namespace, and not global. (In fact the current snippets ain't local, they are Visual Studio local. They are not tied to a specific project type, or even aware of the context they are to be pasted in (eg. #7858)
Yeah you could implement them as a code fix, but to that seem like a lot of code. Just to paste in section of code. In principle the ide and compiler can conspire together and automatically do the work for you.
We could also have usage attributes associated with the template, so that the IDE would be informed to when to suggest the snippet.
If I understand correctly this proposal is a bit like defmacro in Common Lisp. Or is this more like inline T4?
@Ytrog
Bit like inline T4 (minus the active parts), plus the template is validated by compiler.
So you'd get errors at design-time. ie it's not a string but code.
@gafter Is there any limited subset of macros you would be willing to support (e.g. variadic templates)?