﻿# Expedition into Avalonia project

In the age of UI frameworks, legends tell about the great land where a single codebase rules all platforms\. Today, we set foot on the shores of the abandoned mainland known as Avalonia\.

![1260_Avalonia/image1.png](https://import.viva64.com/docx/blog/1260_Avalonia/image1.png)

The mainland of Avalonia is divided into six regions: the Kingdom of Windows, the Free Territories of Linux, the Mountain Strongholds of macOS, the Mobile Principalities of Android, the Coastal Domains of iOS, and the Foggy Archipelago of WebAssembly\. Let's equip ourselves with a static analysis compass and keep a detailed journal of our discoveries\.

## About project

[Avalonia](https://docs.avaloniaui.net) is an open\-source, cross\-platform UI framework for creating applications using \.NET on Windows, macOS, Linux, iOS, Android, and WebAssembly\.

The framework uses its cross\-platform rendering engine to display UI elements, ensuring consistent appearance and behavior across all supported platforms\. This means that developers can share user interface code and maintain a consistent style and functionality across the target platform\.

Avalonia unifies desktop, mobile, and web development using a unique approach that differs from traditional cross\-platform frameworks\. Instead of using native UI elements, Avalonia implements its cross\-platform rendering engine, which provides pixel\-perfect accuracy on all supported platforms\.


> \*\*Note\\\.\*\* We checked this project in 2019\\\. You can read the article about it \[here\]\(https://pvs\-studio\.com/en/blog/posts/csharp/0701/\)\\\.

## Exploring Avalonia

We checked the Avalonia plugin for VS Code\. You can read how to use it in this [documentation](https://pvs-studio.com/en/docs/manual/6646/)\.

### Sacred knowledge of the mainland

Experienced Avalonia guides know clever tricks to shorten their journey\. We'll gather secrets about the use of patterns in C\#\.

Let's take a peek at the `is not null or` construction\.

**Issue 1**

```cpp
public PanelContainerGenerator(ItemsPresenter presenter)
{
  Debug.Assert(presenter.ItemsControl is not null);
  Debug.Assert(presenter.Panel is not null or VirtualizingPanel);
  
  ....
}
```

The PVS\-Studio warning: [V3207](https://pvs-studio.com/en/docs/warnings/v3207/) The 'not null or VirtualizingPanel' logical pattern may not work as expected\. The 'not' pattern is matched only to the first expression from the 'or' pattern\. PanelContainerGenerator\.cs 22

The issue arises because devs have forgotten that the `not` operator has a higher precedence than `or`\. In this construction, the second sub\-expression of the `or` operator becomes meaningless\. For example, in `presenter.Panel is not null or VirtualizingPanel`, if `presenter.Panel` is `null`, checking for `null` will return `true`\. As a result, the second part of the expression has no effect on the final outcome\.

To ensure correct code execution, developers can rewrite the code as follows:

```cpp
public PanelContainerGenerator(ItemsPresenter presenter)
{
  Debug.Assert(presenter.ItemsControl is not null);
  Debug.Assert(presenter.Panel is not (null or VirtualizingPanel));

  ....
}
```

### Mirages: where parameters are altered

On the borders between regions of Avalonia, strange things can happen—passed parameters, like mirages, become distorted and change their meaning\. In this section, we'll look at cases where a method parameter errors\.

**Issue 2**

```cpp
protected override Size MeasureOverride(Size availableSize)
{
  availableSize = new Size(double.PositiveInfinity,
                           double.PositiveInfinity);

  foreach (Control child in Children)
  {
    child.Measure(availableSize);
  }

  return new Size();
}
```

The PVS\-Studio warning: [V3061](https://pvs-studio.com/en/docs/warnings/v3061/) Parameter 'availableSize' is always rewritten in method body before being used\. Canvas\.cs 149

In this example, a parameter is passed to the method but then immediately overwritten\. Looking at the code, we can suggest that the method doesn't need the parameter being passed, or the method's logic is a little messed up\.

**Issue 3**

```cpp
public static async Task<string> Load(string generatedCodeResourceName)
{
  var assembly = typeof(XamlXNameResolverTests).Assembly;
  var fullResourceName = assembly
      .GetManifestResourceNames()
      .First(name => name.Contains("InitializeComponent")
                  && name.Contains("GeneratedInitializeComponent") 
                  && name.EndsWith(generatedCodeResourceName));

  ....
}
```

The PVS\-Studio warning: [V3053](https://pvs-studio.com/en/docs/warnings/v3053/) An excessive expression\. Examine the substrings 'InitializeComponent' and 'GeneratedInitializeComponent'\. InitializeComponentCode\.cs 27

The analyzer highlights a case where the `InitializeComponent` and `GeneratedInitializeComponent` strings are passed to the `Contains` method\. But the first string is a substring of the second one\. So, a call to `Contains` with the second string doesn't impact the condition result\.

### Traps along the path of choices

At the crossroads of Avalonia's paths, stone markers point the way to travelers\. But some of them deceive us\! In this section, we'll explore common mistakes in the project's conditional expressions\.

**Issue 4**

```cpp
public override void Promote(FrugalListBase<T> oldList)
{
  for (var index = 0; index < oldList.Count; ++index)
  {
    if (FrugalListStoreState.Success == Add(oldList.EntryAt(index)))
    {
      continue;
    }

    // this list is smaller than oldList
    throw new ArgumentException($"Cannot promote from '{oldList}' 
      to '{ToString()}'    because the target map is too small.",
      nameof(oldList));
  }
}
```

The PVS\-Studio warning: [V3022](https://pvs-studio.com/en/docs/warnings/v3022/) Expression 'FrugalListStoreState\.Success \=\= Add\(oldList\.EntryAt\(index\)\)' is always true\. FrugalList\.cs 1473

The analyzer reports that the `FrugalListStoreState.Success == Add(oldList.EntryAt(index))` condition is always `true`\. To understand why, let's take a closer look at the `Add` method:

```cpp
public override FrugalListStoreState Add(T value)
{
  ....
  return FrugalListStoreState.Success;
}
```

And here's the issue\. The method always returns the same value, `FrugalListStoreState.Success`\. Since the `if` statement is compared against that value, the condition will always be `true`\.

**Issue 5**

```cpp
private void NotifyingDataSource_CollectionChanged(
object sender,NotifyCollectionChangedEventArgs e)
{
  ....
  if (ShouldAutoGenerateColumns)
  {
     // The columns are also affected (not just rows) 
    //  in this case so we need to reset everything
    _owner.InitializeElements(false /*recycleRows*/);
  }
  ....
}
```

The PVS\-Studio warning: [V3022](https://pvs-studio.com/en/docs/warnings/v3022/) Expression 'ShouldAutoGenerateColumns' is always false\. DataGridDataConnection\.cs 641

And here we go again: we see the same error here\. If we examine the `ShouldAutoGenerateColumns` property, it'll always be `false`, so the code will never be executed in the `if` block\.

```cpp
public bool ShouldAutoGenerateColumns
{
  get
  {
    return false;
  }
}
```

### Ghosts of NullReference 

The darkest corners of Avalonia hide small invisible spirits—the ghosts of `NullReference`\. They hide in the shadows of uninitialized objects and unexpectedly pop up, interrupting the journey at the worst moment\.

**Issue 6**

```cpp
private void InitPicker()
{
  ....
  _hourSelector!.MaximumValue = clock12 ? 12 : 23;
  _hourSelector.MinimumValue = clock12 ? 1 : 0;
  _hourSelector.ItemFormat = "%h";
  var hr = Time.Hours;
  _hourSelector.SelectedValue = !clock12 ? hr : hr > 12 ? hr - 12 
                                              : hr == 0 ? 12 : hr;
  ....
  _hourSelector?.Focus(NavigationMethod.Pointer);
}
```

The PVS\-Studio warning: [V3095](https://pvs-studio.com/en/docs/warnings/v3095/) The '\_hourSelector' object was used before it was verified against null\. Check lines: 260, 283\. TimePickerPresenter\.cs 260

The `_hourSelector` variable appears several times in this fragment\. Devs even use the `!` operator, showing confidence that the variable can't possibly be `null`\. Only at the end, devs decide to check for `null`\.

### Code typos

One wrong character, and the specified path will lead you to impassable swamps instead of the enchanting valley\. PVS\-Studio analyzer has detected a typical typo in the code\. Let's examine this error and see how many times it has appeared\.

**Issue 7**

```cpp
public ImmutableRadialGradientBrush(RadialGradientBrush source) : base(source)
{
  Center = source.Center;
  GradientOrigin = source.GradientOrigin;
  RadiusX = source.RadiusX;
  RadiusY = source.RadiusX;
}
```

The PVS\-Studio warning: [V3056](https://pvs-studio.com/en/docs/warnings/v3056/) Consider reviewing the correctness of 'RadiusX' item's usage\. ImmutableRadialGradientBrush\.cs 74

Due to a typo, devs write the `source.RadiusX` value to the `RadiusY` property—and also to the `RadiusX` property\. 

It seems that the code logic is violated here\. Judging by the code, if we use this brush, the gradient will always be circular and never oval\. This code may cause unexpected behavior when using the framework\.

### Sleeping guardians of the mainland

Tests are the guardians that protect the stability of the mainland\. But what happens when they fall asleep on duty and prove to be unreliable? In this section, the analyzer has detected another issue:

**Issue 8**

```cpp
[Fact]
public void Content_Can_Be_TopLeft_Aligned()
{
  Border content;
  var target = new ContentPresenter
  {
    Content = content = new Border
    {
      MinWidth = 16,
      MinHeight = 16,
      HorizontalAlignment = HorizontalAlignment.Right,
    },
  };

  target.UpdateChild();
  target.Measure(new Size(100, 100));
  target.Arrange(new Rect(0, 0, 100, 100));

  Assert.Equal(new Rect(84, 0, 16, 16), content.Bounds);
}

[Fact]
public void Content_Can_Be_TopRight_Aligned()
{
  Border content;
  var target = new ContentPresenter
  {
    Content = content = new Border
    {
      MinWidth = 16,
      MinHeight = 16,
      HorizontalAlignment = HorizontalAlignment.Right,
    },
  };

  target.UpdateChild();
  target.Measure(new Size(100, 100));
  target.Arrange(new Rect(0, 0, 100, 100));

  Assert.Equal(new Rect(84, 0, 16, 16), content.Bounds);
}
```

The PVS\-Studio warning: [V3013](https://pvs-studio.com/en/docs/warnings/v3013/) It is odd that the body of 'Content\_Can\_Be\_TopLeft\_Aligned' function is fully equivalent to the body of 'Content\_Can\_Be\_TopRight\_Aligned' function\. ContentPresenterTests\_Layout\.cs 169

This code fragment contains two identical test methods\. Why use two methods if they test the same thing? Devs might have wanted to check the indents on the left and right, but the tests are identical, so they only check the indents on one side\.

### Codex of Clean Code: from chaos to order

The heart of Avalonia keeps the tomes inscribed with the Codex of Clean Code, a voluminous set of rules\. They help travelers stay on the right path\. 

In this section, we'll look at the analyzer warning that may lead to incorrect program behavior\. To avoid this, devs should support high\-quality and clean code\.

**Issue 9**

```cpp
private static RuleResult LB25(ReadOnlySpan<char> text, LineBreakState state)
{
  switch (state.Next(text).LineBreakClass)
  {
    ....
    // [25.03] NU(SY|IS)* CL × PR
    case LineBreakClass.ClosePunctuation:
    {
      switch (state.Previous.LineBreakClass)
      {
        case LineBreakClass.Numeric:  // <=
        {
          return RuleResult.NoBreak;
        }
        case LineBreakClass.BreakSymbols:
        case LineBreakClass.InfixNumeric:
        {
          if (state.Previous.LineBreakClass == LineBreakClass.Numeric)  // <=
          {
            return RuleResult.NoBreak;
          }

          break;
        }
      }

      break;
    }
    ....
  }
}
```

The PVS\-Studio warning: [V3022](https://pvs-studio.com/en/docs/warnings/v3022/) Expression 'state\.Previous\.LineBreakClass \=\= LineBreakClass\.Numeric' is always false\. LineBreakEnumerator\.cs 963

This method contains ten `switch-case` constructions—some of them are nested up to four times\! It's not surprising that it's easy to make mistakes in such a cumbersome code—that's exactly what happened here\. Devs attempt to compare the value with `LineBreakClass.Numeric` twice in one `switch` block\. The first comparison works, but the second one never does because the `case` has already been matched earlier\.

That's a classic among errors: the code just needs to be refactored\. You can read about similar cases in another [article](https://pvs-studio.com/en/blog/posts/1237/)\. We may notice that the `switch-case` block is a little messy, and the `case` blocks contain duplicated actions since it's expected that `LineBreakClass.Numeric` will return `RuleResult.NoBreak`—just like in the earlier block\.

## Expedition's final destination

Compared to the last review, the project has made impressive progress\. This framework remains popular with users and has become even more reliable and stable over time\.

However, some errors detected by PVS\-Studio analyzer can lead to unexpected program behavior and impact user experience\. I hope this article helps developers spot and fix bugs, which will enhance code quality\.

If you're interested in checking your own project, you can try [PVS\-Studio analyzer](https://pvs-studio.com/en/pvs-studio/try-free/)\.