Introduction
I am working on the CsConsoleFormat library, which prints hierarchical documents to the console. XAML and APIs are akin to LINQ to XML for building documents. I need a clean, short, understandable API, since the main goal of the library is to make complex formatting simple. Moreover, the API should, whenever possible, encourage the writing of readable code .
The System.Xml.Linq.XElement class has a constructor that takes a params object[] content argument and applies the following transformations to it: collapse sequences into elements, skip null, convert strings to text nodes, add elements and attributes as is, convert everything else to rows.
Problem
My library relies on the same approach with params object[] content , but instead of the constructor, the AddChildren method is AddChildren . The reasons for this are as follows:
Constructors with arguments like
params object[] contentmay seem unnatural to those working with XAML. The problem becomes more acute, if I add meaningful constructors, I have to add this argument to all constructors.Unlike
XElement, my elements have full-fledged properties that are conveniently written in the initializer. And ifnew Span("Yellow") { Color = Yellow }looks normal, then when a hierarchy appears, the logical sequence is lost:new Document( new Span("Title") { Color = White }, new Grid() { // ... очень длинный документ } new Span("Footer") ) { Color = Yellow, }Suddenly, after reading the entire document, you notice that all the elements inherited the yellow color from the root element. Moreover, the library supports attached properties (for example,
Grid.Column), which can be set only after calling the constructor (using theSetextension method).When using the
AddChildrenmethodAddChildrenlogical sequence is preserved:new Document { Color = Yellow, }.AddChildren( new Span("Title") { Color = White }, new Grid { /* ... */ } .AddChildren( // ... очень длинный документ ) new Span("Footer") )However, if only
AddChildren, then a short code like this:new Div(DateTime.Now), new Div(DateTime.Now) { TextAlign = Left },becomes more verbose:
new Div().AddChildren(DateTime.Now), new Div { TextAlign = Left }.AddChildren(DateTime.Now),
Questions
Is there an approach that combines as many advantages as possible and avoids as many of the above as possible?
If I decide to include both constructors and
AddChildren, is there any way to force / motivate the encoder to write code in accordance with the style described above (designer for single-line children, method for complex content)? Roslyn code analyzers for this fit?
Notes
- I know of an even simpler API - extension methods a la npm / colors . However, it seems that this method is applicable only in a narrow subset of cases, so the problem remains.
Cross-post: API design issues - Implementing API to XML with elements having properties .