-
Notifications
You must be signed in to change notification settings - Fork 11
Extensibility and plugin mechanism
The library has two goals:
-
Return a string rendering of an expression tree (or other related type)
-
Optionally, identify which part of the string corresponds to which part of the expression tree. (This powers the selection sync of ExpressionTreeVisualizer):
The library comes with a number of built-in renderers. However, you can register additional renderers, identified with a string
key, by calling the ExpressionTreeToString.Renderers.Register
method, and passing in:
-
the renderer key, and
-
the renderer itself, matching the following delegate type:
public delegate (string result, Dictionary<string, (int start, int length)>? pathSpans) Writer( object o, OneOf<string, Language?> languageArg, bool usePathSpans );
Like so:
// using ExpressionTreeToString
Renderers.Register(
"DummyRenderer",
(o, language, usePathSpans) => "Hello world!", null)
);
Calling the ToString
extension method with the renderer key will return the generated string:
// using ExpressionTreeToString
Expression<Func<bool>> expr = () => true;
var s = expr.ToString("DummyRenderer");
/*
Hello world!
*/
You can generate the string representation however you like -- in the above (contrived) example, it's always returning the same string. However, the library also provides a set of base classes, to ease the implementation.
The Renderer
delegate takes three parameters:
-
o
-- the object to provide a string representation for -
languageArg
-- either a string ("C#"
,"Visual Basic"
) orLanguage
enum value, which describes the code language to use when rendering literals and type name constructs -
usePathSpans
--true
if you need to generate the dictionary with the text spans corresponding to each property path
and should return a value tuple with two members:
- the representation
- if
usePathSpans
istrue
, the property path / spansDictionary<string, (int start, int length)>
ifusePathSpans
isfalse
, thennull
For example:
// using ExpressionTreeToString
Renderers.Register(
"Factory methods",
(o, language, usePathSpans) =>
usePathSpans ?
(new FactoryMethodsWriterVisitor(o, languageArg, out var pathSpans).ToString(), pathSpans) :
(new FactoryMethodsWriterVisitor(o, languageArg).ToString(), null)
);
NB. I haven't decided if it should be possible to override existing renderers with the same key. This behavior is currently unsupported.
Although the library doesn't dictate to you how to produce the string representation or the path spans, it exposes additional classes for visiting the nodes of the expression tree and writing the representation, at various levels of abstraction.
Class | Base class | Description |
---|---|---|
WriterVisitorBase |
Low-level writing methods, and WriteNode . Each call to WriteNode adds an entry to the pathSpans . |
|
BuiltinsWriterVisitor |
WriterVisitorBase |
Provides abstract methods that force an implementation for each type in the System.Linq.Expressions namespace |
CodeWriterVisitor |
BuiltinsWriterVisitor |
Common functionality for rendering an expression tree as code |
You can also inherit from one of the visitor-writer classes used by the built-in renderers:
CSharpWriterVisitor
VBWriterVisitor
FactoryMethodsWriterVisitor
ObjectNotationWriterVisitor
TextualTreeWriterVisitor
Which class you should inherit from, depends on what you're trying to do:
- If you don't care about the built-in expression types at all, inherit from
WriterBase
. When a given node is passed toWriteNode
, the implementation would forward the child expressions of the node to recursively callWriteNode
. - If you want to handle the built-in expression types in a new way unrelated to existing renderers -- say XML or JSON data extracted from the expression tree -- inherit from
BuiltinsWriterVisitor
. - If you want to extend an existing writer-visitor, you can do so, optionally overriding the base methods as needed; for example, in order to handle various extension expression types.
The API for loading renderers from third-party DLLs hasn't yet been implemented, and will probably look something like this.