diff --git a/src/Avalonia.Base/Styling/DescendentSelector.cs b/src/Avalonia.Base/Styling/DescendentSelector.cs
index a056f3ba8b..77ae0f2877 100644
--- a/src/Avalonia.Base/Styling/DescendentSelector.cs
+++ b/src/Avalonia.Base/Styling/DescendentSelector.cs
@@ -13,7 +13,7 @@ namespace Avalonia.Styling
public DescendantSelector(Selector? parent)
{
- _parent = parent ?? throw new InvalidOperationException("Descendant selector must be preceeded by a selector.");
+ _parent = parent ?? throw new InvalidOperationException("Descendant selector must be preceded by a selector.");
}
///
diff --git a/src/Avalonia.Base/Styling/OrSelector.cs b/src/Avalonia.Base/Styling/OrSelector.cs
index 3b0aa03492..53e4baa2c4 100644
--- a/src/Avalonia.Base/Styling/OrSelector.cs
+++ b/src/Avalonia.Base/Styling/OrSelector.cs
@@ -10,7 +10,7 @@ namespace Avalonia.Styling
///
/// The OR style selector.
///
- internal class OrSelector : Selector
+ internal sealed class OrSelector : Selector
{
private readonly IReadOnlyList _selectors;
private string? _selectorString;
@@ -42,18 +42,7 @@ namespace Avalonia.Styling
public override bool IsCombinator => false;
///
- public override Type? TargetType
- {
- get
- {
- if (_targetType == null)
- {
- _targetType = EvaluateTargetType();
- }
-
- return _targetType;
- }
- }
+ public override Type? TargetType => _targetType ??= EvaluateTargetType();
///
public override string ToString(Style? owner)
@@ -71,7 +60,9 @@ namespace Avalonia.Styling
var activators = new OrActivatorBuilder();
var neverThisInstance = false;
- for (var i = 0; i < _selectors.Count; i++)
+ var count = _selectors.Count;
+
+ for (var i = 0; i < count; i++)
{
var match = _selectors[i].Match(control, parent, subscribe);
@@ -108,7 +99,9 @@ namespace Avalonia.Styling
internal override void ValidateNestingSelector(bool inControlTheme)
{
- for (var i = 0; i < _selectors.Count; i++)
+ var count = _selectors.Count;
+
+ for (var i = 0; i < count; i++)
{
_selectors[i].ValidateNestingSelector(inControlTheme);
}
@@ -118,7 +111,9 @@ namespace Avalonia.Styling
{
Type? result = null;
- for (var i = 0; i < _selectors.Count; i++)
+ var count = _selectors.Count;
+
+ for (var i = 0; i < count; i++)
{
var selector = _selectors[i];
if (selector.TargetType == null)
diff --git a/src/Avalonia.Base/Styling/TypeNameAndClassSelector.cs b/src/Avalonia.Base/Styling/TypeNameAndClassSelector.cs
index f8670cfdb3..4e0d37479b 100644
--- a/src/Avalonia.Base/Styling/TypeNameAndClassSelector.cs
+++ b/src/Avalonia.Base/Styling/TypeNameAndClassSelector.cs
@@ -11,7 +11,7 @@ namespace Avalonia.Styling
/// A selector that matches the common case of a type and/or name followed by a collection of
/// style classes and pseudoclasses.
///
- internal class TypeNameAndClassSelector : Selector
+ internal sealed class TypeNameAndClassSelector : Selector
{
private readonly Selector? _previous;
private List? _classes;
@@ -85,12 +85,7 @@ namespace Avalonia.Styling
///
public override string ToString(Style? owner)
{
- if (_selectorString == null)
- {
- _selectorString = BuildSelectorString(owner);
- }
-
- return _selectorString;
+ return _selectorString ??= BuildSelectorString(owner);
}
///
diff --git a/tests/Avalonia.Benchmarks/Properties/launchSettings.json b/tests/Avalonia.Benchmarks/Properties/launchSettings.json
new file mode 100644
index 0000000000..619d635ebf
--- /dev/null
+++ b/tests/Avalonia.Benchmarks/Properties/launchSettings.json
@@ -0,0 +1,15 @@
+{
+ "profiles": {
+ "Avalonia.Benchmarks": {
+ "commandName": "Project"
+ },
+ "Avalonia.Benchmarks (in-process)": {
+ "commandName": "Project",
+ "commandLineArgs": "--inprocess"
+ },
+ "Avalonia.Benchmarks (debug)": {
+ "commandName": "Project",
+ "commandLineArgs": "--debug"
+ }
+ }
+}
diff --git a/tests/Avalonia.Benchmarks/Styling/SelectorBenchmark.cs b/tests/Avalonia.Benchmarks/Styling/SelectorBenchmark.cs
index 0ac96c1103..11bc5ce35f 100644
--- a/tests/Avalonia.Benchmarks/Styling/SelectorBenchmark.cs
+++ b/tests/Avalonia.Benchmarks/Styling/SelectorBenchmark.cs
@@ -1,4 +1,5 @@
-using Avalonia.Controls;
+using System;
+using Avalonia.Controls;
using Avalonia.Styling;
using BenchmarkDotNet.Attributes;
@@ -11,6 +12,8 @@ namespace Avalonia.Benchmarks.Styling
private readonly Calendar _matchingControl;
private readonly Selector _isCalendarSelector;
private readonly Selector _classSelector;
+ private readonly Selector _orSelectorTwo;
+ private readonly Selector _orSelectorFive;
public SelectorBenchmark()
{
@@ -23,6 +26,14 @@ namespace Avalonia.Benchmarks.Styling
_isCalendarSelector = Selectors.Is(null);
_classSelector = Selectors.Class(null, className);
+
+ _orSelectorTwo = Selectors.Or(new AlwaysMatchSelector(), new AlwaysMatchSelector());
+ _orSelectorFive = Selectors.Or(
+ new AlwaysMatchSelector(),
+ new AlwaysMatchSelector(),
+ new AlwaysMatchSelector(),
+ new AlwaysMatchSelector(),
+ new AlwaysMatchSelector());
}
[Benchmark]
@@ -48,5 +59,40 @@ namespace Avalonia.Benchmarks.Styling
{
return _classSelector.Match(_matchingControl);
}
+
+ [Benchmark]
+ public SelectorMatch OrSelector_One_Match()
+ {
+ return _orSelectorTwo.Match(_matchingControl);
+ }
+
+ [Benchmark]
+ public SelectorMatch OrSelector_Five_Match()
+ {
+ return _orSelectorFive.Match(_matchingControl);
+ }
+ }
+
+ internal class AlwaysMatchSelector : Selector
+ {
+ public override bool InTemplate => false;
+
+ public override bool IsCombinator => false;
+
+ public override Type TargetType => null;
+
+ public override string ToString(Style owner)
+ {
+ return "Always";
+ }
+
+ protected override SelectorMatch Evaluate(StyledElement control, IStyle parent, bool subscribe)
+ {
+ return SelectorMatch.AlwaysThisType;
+ }
+
+ protected override Selector MovePrevious() => null;
+
+ protected override Selector MovePreviousOrParent() => null;
}
}