Would a Declarative Reactive UI be too expensive?

Not 100% sure "reactive" is the right term. But frameworks like Flutter, where they rebuild sections of the UI with light weight objects when there is a state change. Would something like that work well in C#? Thinking of for like UI heavy games? It feels like it would generate a lot of garbage so not really make sense to do. But I also see there is like MAUI Reactor. Which seems to do it. But seems like it wouldn't scale well. And doesn't seem like they are doing any pooling or anything. It is one of those things that are a bit hard to setup a realistic MVP to profile myself.
21 Replies
Klarth
Klarth2d ago
Declarative UI, especially in .NET, is a joke IMO. Only major upside is you get to use your language's built-in tooling. In .NET, you are churning objects which is bad for GC. Even worse if you're churning UI controls. "Lightweight" UI controls don't really exist.
MechWarrior99
MechWarrior99OP2d ago
That is kinda what I was assuming the case would be. The big upside that I like is that it makes 'binding' so easy and straight forward, more like immediate mode UIs.
Klarth
Klarth2d ago
Comet, which was going to be MVU for Maui, broke MVU immutability rules and then the author moved from MS to Meta to some AI frontend place. I don't see how XAML bindings are difficult.
MechWarrior99
MechWarrior99OP2d ago
They aren't difficult, they just feel really clunky to me. Basically, I just like the MVU pattern better. I guess you could do it with pooling, and 'resetting' the elements/widgets when releasing them. But I guess it would still be rather slower than just doing the normal way of updating elements.
Klarth
Klarth2d ago
Keep in mind that XAML has some serious space for optimizations that C# can't do. So if you're using UWP (or presumably WinUI3), then there are a lot of perf improvements you're losing by moving your UI into C#. See https://discord.com/channels/143867839282020352/169726357331378176/1298419243033034894
MODiX
MODiX2d ago
Sergio
Think of it this way. Suppose you have this:
<Grid Height="100" Width="200">
<Button Text="Hello" Grid.Row="0" />
</Grid>
<Grid Height="100" Width="200">
<Button Text="Hello" Grid.Row="0" />
</Grid>
And this:
Grid grid = new() { Height = 100, Width = 200 };
Button button = new() { Text = "Hello" };
Grid.SetRow(button, 0);
grid.Children.Add(button);
Grid grid = new() { Height = 100, Width = 200 };
Button button = new() { Text = "Hello" };
Grid.SetRow(button, 0);
grid.Children.Add(button);
When you use C#, you do the following: - Create the managed Grid object - This then creates and activates the native object - Calls set_Height and go through the marshalling - Call set_Width and go through the marshalling - Create the managed Button object - Create and activate the native button - Call set_Text and marshal the string ... Whereas if you use XAML, it loads from XBF2. This is a highly optimized binary format, that does something like: - Create this native object with this special id. Do no checks. - Read these 8 bytes as int-s, assign to these two properties with this raw index in this table. Do no checks, just trust me. - Do the same for Button - Just set this property (Text) from this known id, assign directly this value from this binary blob. Again just trust me. ... It's uber fast, does 0 validation, can use a special high-perf activation path for WinRT, does 0 marshalling, creates 0 managed objects entirely. It's just meant to be fast.
Quoted by
<@160638834109972480> from #gui (click here)
React with ❌ to remove this embed.
Klarth
Klarth2d ago
Well fine then Modix, embed the whole thing. :harold:
MechWarrior99
MechWarrior99OP2d ago
Haha, it is actually just for doing a custom UI framework (for fun and learning new things)
Klarth
Klarth2d ago
Of course, other XAML frameworks aren't implemented as native, so you only get part of those benefits.
MechWarrior99
MechWarrior99OP2d ago
Interesting to know about how XAML does fancy stuff to be faster. Good to know!
Klarth
Klarth2d ago
Well, the Avalonia PropertyStore (system which manages bindings, setter priorities, etc) is built around observables. So you can do a lot with reactive programming. Avalonia has https://github.com/AvaloniaUI/Avalonia.Markup.Declarative Uno has https://platform.uno/c-markup/ In case you need inspiration. I still personally just don't like it. 🤷‍♂️
MechWarrior99
MechWarrior99OP2d ago
I appreciate it! the links! Fair enough, to each their own really. If you take declarative UI out of the equation. Any thoughts on MVU pattern and implementing it?
Klarth
Klarth2d ago
No idea. I saw the pattern and I'm not willing to put real time behind unproven stuff in .NET, especially if it has obvious flaws (object churn). Uno has its own adaptation called MVUX: https://platform.uno/docs/articles/external/uno.extensions/doc/Overview/Mvux/Overview.html?tabs=viewmodel%2Cmodel Not entirely related, but Avalonia allows you to bind directly to observables. Though it's still a one-way road.
MechWarrior99
MechWarrior99OP2d ago
Fair enough. I'm still struggling to fully grasp observables, which probably doesn't help some stuff
Klarth
Klarth2d ago
I assume the pattern can be smarter with pooling (and the limitations that would come from that) and make MVU reasonable with said enforced pattern. But that's a lot of invested time. Time that I prefer the pattern architect to put in, not me.
MechWarrior99
MechWarrior99OP2d ago
Yeah, makes sense.
Klarth
Klarth2d ago
Yeah, observables are awkward. I need to invest time into R3 basics at some point. Largely same thing, but different implementation. https://github.com/Cysharp/R3 The unit tests seem really clean and concise. Which helps understanding how certain features work.
MechWarrior99
MechWarrior99OP2d ago
Cool, I'll take a look. That is one thing that has made it hard for me is finding actual examples of observables that didn't have a bunch of other unrelated stuff tacked on that was doing stuff behind the scenes.
Klarth
Klarth2d ago
Like if you don't understand what CombineLatest does, you can always look at https://github.com/Cysharp/R3/blob/main/tests/R3.Tests/OperatorTests/CombineLatestTest.cs And you'll see it emits a new item (containing the latest value of each input) each time one of the input streams pushes a new item. Well, once each input stream has pushed at least one item.
MechWarrior99
MechWarrior99OP2d ago
Oh weird... (Obserivables, not the implementation)
Klarth
Klarth2d ago
Most of them are 1:1 with System.Reactive though there are some new operators, too.

Did you find this page helpful?