Stylish Avalonia
- Tutorial
Here’s a quick look at algorithm vectorization capabilities in .NET Framework and .NET Core. This article is for those who know nothing about these techniques. I will also show that .NET doesn’t actually lag behind "real compiled" languages for native development.
User interfaces of modern enterprise applications are quite complex. You, as a developer, often need to implement in-app navigation, validate user input, show or hide screens based on user preferences. For better UX, your app should be capable of saving state to the disk when the app is suspending and of restoring state when the app is resuming.
ReactiveUI provides facilities allowing you to persist application state by serializing the view model tree when the app is shutting down or suspending. Suspension events vary per platform. ReactiveUI uses the Exit
event for WPF, ActivityPaused
for Xamarin.Android, DidEnterBackground
for Xamarin.iOS, OnLaunched
for UWP.
In this tutorial we are going to build a sample application which demonstrates the use of the ReactiveUI Suspension feature with Avalonia — a cross-platform .NET Core XAML-based GUI framework. You are expected to be familiar with the MVVM pattern and with reactive extensions before reading this note. Steps described in the tutorial should work if you are using Windows 10 or Ubuntu 18 and have .NET Core SDK installed. Let's get started!
In my previous article I described how to achieve the "Maybe" monad behavior using async/await operators. This time I am going to show how to implement another popular design pattern "Reader Monad" using the same techniques.
That pattern allows implicit passing some context into some function without using function parameters or shared global objects and it can be considered as yet another way to implement dependency injection. For example:
class Config { public string Template; }
public static async Task Main()
{
Console.WriteLine(await GreetGuys().Apply(new Config {Template = "Hi, {0}!"}));
//(Hi, John!, Hi, Jose!)
Console.WriteLine(await GreetGuys().Apply(new Config {Template = "¡Hola, {0}!" }));
//(¡Hola, John!, ¡Hola, Jose!)
}
//These functions do not have any link to any instance of the Config class.
public static async Reader<(string gJohn, string gJose)> GreetGuys()
=> (await Greet("John"), await Greet("Jose"));
static async Reader<string> Greet(string name)
=> string.Format(await ExtractTemplate(), name);
static async Reader<string> ExtractTemplate()
=> await Reader<string>.Read<Config>(c => c.Template);
Generalized async return types — it is a new C#7 feature that allows using not only Task as a return type of async methods but also other types (classes or structures) that satisfy some specific requirements.
At the same time, async/await is a way to call a set of "continuation" functions inside some context which is an essence of another design pattern — Monad. So, can we use async/await to write a code which will behave in the same way like if we used monads? It turns out that — yes (with some reservations). For example, the code below is compilable and working:
async Task Main()
{
foreach (var s in new[] { "1,2", "3,7,1", null, "1" })
{
var res = await Sum(s).GetMaybeResult();
Console.WriteLine(res.IsNothing ? "Nothing" : res.GetValue().ToString());
}
// 3, 11, Nothing, Nothing
}
async Maybe<int> Sum(string input)
{
var args = await Split(input);//No result checking
var result = 0;
foreach (var arg in args)
result += await Parse(arg);//No result checking
return result;
}
Maybe<string[]> Split(string str)
{
var parts = str?.Split(',').Where(s=>!string.IsNullOrWhiteSpace(s)).ToArray();
return parts == null || parts.Length < 2 ? Maybe<string[]>.Nothing() : parts;
}
Maybe<int> Parse(string str)
=> int.TryParse(str, out var result) ? result : Maybe<int>.Nothing();
Further, I will explain how the code works...
After working on different projects, I've noticed that every one of them had some common problems, regardless of domain, architecture, code convention and so on. Those problems weren't challenging, just a tedious routine: making sure you didn't miss anything stupid and obvious. Instead of doing this routine on a daily basis I became obsessed with seeking solution: some development approach or code convention or whatever that will help me to design a project in a way that will prevent those problems from happening, so I can focus on interesting stuff. That's the goal of this article: to describe those problems and show you that mix of tools and approaches that I found to solve them.
Beginning with C# 8.0 on .NET Core 3.0, you can define an implementation when you declare a member of an interface. The most common scenario is to safely add members to an interface already released and used by innumerable clients.
In this tutorial, you'll learn how to: