→ Text and video in Russian
Make it easier to get finished: Interview with John Romero, developer of Doom
→ Text and video in Russian
The art of creating computer programs
>>> class A:
... x = 2
...
>>> A.x
2
>>> A().x
2
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! Source code of the app described in this tutorial is available on GitHub.
if constexpr
and generic lambdas so that code could use the type if it is defined, while still being accepted by the compiler (and being discarded) if the type is not defined.struct
all the time.struct
technique with an unqualified name. You can’t use it to probe a type that you didn’t import into the current namespace.Some time ago, in a discussion on one of SObjectizer's releases, we were asked: "Is it possible to make a DSL to describe a data-processing pipeline?" In other words, is it possible to write something like that:
A | B | C | D
and get a working pipeline where messages are going from A to B, and then to C, and then to D. With control that B receives exactly that type that A returns. And C receives exactly that type that B returns. And so on.
It was an interesting task with a surprisingly simple solution. For example, that's how the creation of a pipeline can look like:
auto pipeline = make_pipeline(env, stage(A) | stage(B) | stage(C) | stage(D));
Or, in a more complex case (that will be discussed below):
auto pipeline = make_pipeline( sobj.environment(),
stage(validation) | stage(conversion) | broadcast(
stage(archiving),
stage(distribution),
stage(range_checking) | stage(alarm_detector{}) | broadcast(
stage(alarm_initiator),
stage( []( const alarm_detected & v ) {
alarm_distribution( cerr, v );
} )
)
) );
In this article, we'll speak about the implementation of such pipeline DSL. We'll discuss mostly parts related to stage()
, broadcast()
and operator|()
functions with several examples of usage of C++ templates. So I hope it will be interesting even for readers who don't know about SObjectizer (if you never heard of SObjectizer here is an overview of this tool).
\
symbol in regular string have special meaning. \t
is tab character, \r
is carriage return and so on.r'\t'
is just backslash and t
.'
inside r'...'
. However, it still can be escaped by \
, but \
is preserved in the string:QueryProvider can’t deal with this:
var result = _context.Humans
.Select(x => $"Name: {x.Name} Age: {x.Age}")
.Where(x => x != "")
.ToList();
It can’t deal with any sentence using an interpolated string, but it’ll easily deal with this:
var result = _context.Humans
.Select(x => "Name " + x.Name + " Age " + x.Age)
.Where(x => x != "")
.ToList();
The most painful thing is to fix bugs after turning on ClientEvaluation (exception for client-side calculation), since all Automapper profiles should be strictly analyzed for interpolation. Let’s find out what’s what and propose our solution to the problem.
It’s time to talk about exceptions or, rather, exceptional situations. Before we start, let’s look at the definition. What is an exceptional situation?
This is a situation that makes the execution of current or subsequent code incorrect. I mean different from how it was designed or intended. Such a situation compromises the integrity of an application or its part, e.g. an object. It brings the application into an extraordinary or exceptional state.
But why do we need to define this terminology? Because it will keep us in some boundaries. If we don’t follow the terminology, we can get too far from a designed concept which may result in many ambiguous situations. Let’s see some practical examples:
struct Number
{
public static Number Parse(string source)
{
// ...
if(!parsed)
{
throw new ParsingException();
}
// ...
}
public static bool TryParse(string source, out Number result)
{
// ..
return parsed;
}
}
This example seems a little strange, and it is for a reason. I made this code slightly artificial to show the importance of problems appearing in it. First, let’s look at the Parse
method. Why should it throw an exception?