-
Notifications
You must be signed in to change notification settings - Fork 10k
New issue
Have a question about this project? Sign up for a free GitHub account to open an issue and contact its maintainers and the community.
By clicking “Sign up for GitHub”, you agree to our terms of service and privacy statement. We’ll occasionally send you account related emails.
Already on GitHub? Sign in to your account
[Blazor] Why isn't RenderTreeBuilder using CallerLineNumber for sequence numbers? #10890
Comments
@egil Thanks for this suggestion! We'll take a look to see if this is something we can address. |
A little feedback after using my own extension class for a while. Extracting line numbers makes the code much less sensitive to being rearranged and refactored, but it also hides the explicit need for sequence numbers need to follow certain rules. If somebody creates helper functions/helper class that calls the It might be possible to have a debug mode in Here is my current extension method: internal static class RenderTreeBuilderExtensions
{
private const int DEFAULT_SEQUENCE = -1;
public static void OpenElement(this RenderTreeBuilder builder, string elementName, [CallerLineNumber] int sequence = DEFAULT_SEQUENCE)
{
builder.OpenElement(sequence, elementName);
}
public static void OpenComponent<TComponent>(this RenderTreeBuilder builder, [CallerLineNumber] int sequence = DEFAULT_SEQUENCE) where TComponent : IComponent
{
builder.OpenComponent<TComponent>(sequence);
}
public static void AddAttribute(this RenderTreeBuilder builder, string name, string? value, [CallerLineNumber] int sequence = DEFAULT_SEQUENCE)
{
if (string.IsNullOrEmpty(value)) return;
builder.AddAttribute(sequence, name, value);
}
public static void AddEventListener<T>(this RenderTreeBuilder builder, string eventName, EventCallback<T> callback, [CallerLineNumber] int sequence = DEFAULT_SEQUENCE)
{
if (string.IsNullOrEmpty(eventName)) return;
builder.AddAttribute(sequence, eventName, callback);
}
public static void AddAttribute(this RenderTreeBuilder builder, string name, bool value, [CallerLineNumber] int sequence = DEFAULT_SEQUENCE)
{
builder.AddAttribute(sequence, name, value);
}
public static void AddAttribute(this RenderTreeBuilder builder, string name, MulticastDelegate value, [CallerLineNumber] int sequence = DEFAULT_SEQUENCE)
{
builder.AddAttribute(sequence, name, value);
}
public static void AddAttribute(this RenderTreeBuilder builder, string name, object? value, [CallerLineNumber] int sequence = DEFAULT_SEQUENCE)
{
if (value is null) return;
builder.AddAttribute(sequence, name, value);
}
public static void AddMultipleAttributes<T>(this RenderTreeBuilder builder, IEnumerable<KeyValuePair<string, T>>? attributes, [CallerLineNumber] int sequence = DEFAULT_SEQUENCE)
{
if (attributes is null) return;
builder.AddMultipleAttributes<T>(sequence, attributes);
}
public static void AddContent(this RenderTreeBuilder builder, string textContent, [CallerLineNumber] int sequence = DEFAULT_SEQUENCE)
{
builder.AddContent(sequence, textContent);
}
public static void AddContent(this RenderTreeBuilder builder, RenderFragment fragment, [CallerLineNumber] int sequence = DEFAULT_SEQUENCE)
{
builder.AddContent(sequence, fragment);
}
public static void AddMarkupContent(this RenderTreeBuilder builder, string markupContent, [CallerLineNumber] int sequence = DEFAULT_SEQUENCE)
{
builder.AddMarkupContent(sequence, markupContent);
}
public static void AddIdAttribute(this RenderTreeBuilder builder, string? value, [CallerLineNumber] int sequence = DEFAULT_SEQUENCE)
{
if (string.IsNullOrEmpty(value)) return;
builder.AddAttribute(sequence, "id", value);
}
public static void AddClassAttribute(this RenderTreeBuilder builder, string? value, [CallerLineNumber] int sequence = DEFAULT_SEQUENCE)
{
if (string.IsNullOrEmpty(value)) return;
builder.AddAttribute(sequence, "class", value);
}
public static void AddRoleAttribute(this RenderTreeBuilder builder, string? value, [CallerLineNumber] int sequence = DEFAULT_SEQUENCE)
{
if (string.IsNullOrEmpty(value)) return;
builder.AddAttribute(sequence, "role", value);
}
public static void AddAriaLabelAttribute(this RenderTreeBuilder builder, string? value, [CallerLineNumber] int sequence = DEFAULT_SEQUENCE)
{
if (string.IsNullOrEmpty(value)) return;
builder.AddAttribute(sequence, "aria-label", value);
}
public static void AddAriaHiddenAttribute(this RenderTreeBuilder builder, bool value = true, [CallerLineNumber] int sequence = DEFAULT_SEQUENCE)
{
builder.AddAttribute(sequence, "aria-hidden", value);
}
} Ps. related to this discussion is the following issue: #11177 |
I was initially really keen on doing this - it's a very good suggestion! However, after reflecting on it more, I came to the view that it would be dangerous to do this. If developers aren't aware that sequence numbers exist and are being used, they will not understand why they can't do things like this:
... where
Yes, exactly!
I'm not convinced we have a viable proposal here. Adding strange behaviors like "your code may break if you factor out parts of it into a different file" is completely alien to C# developers. If people don't even see the sequence numbers in their own code, they will have no idea why this breaks things or what they are supposed to do about it. Altogether I'm now of the view that having the sequence numbers appear explicitly is the only safe approach, as it forces developers to recognize what's going on and allow for it. I'll close this as we're not realistically in a position to do this for 3.0 anyway. We could reconsider in the future if a much clearer and more understandable design emerges. Hope that makes sense! |
Thanks. Makes sense. I'll keep using my internal extension methods, and see where it lands in the future. |
Hi all,
I understand we need sequence numbers to efficiently diff the render tree, but I am wondering why the
CallerLineNumber
attribute cannot be used on methods in theRenderTreeBuilder
class to get the result we need, without the maintainability issues related to hardcoded sequence numbers.As I understand it, the compiler will embed the line number at compile time in the assembly, when it sees the
CallerLineNumber
attribute, thus, performance should be comparable to us manually hardcoding sequence numbers.Here is a small demonstration of its use, through a bunch of extensions methods I've created:
That allows me to write code that uses
RenderTreeBuilder
class like so:The text was updated successfully, but these errors were encountered: