From 8632f42ef1c3320a69b6e828edb5a6fc8b7cbc5c Mon Sep 17 00:00:00 2001 From: Sebastien Marichal Date: Mon, 5 Jun 2023 17:37:15 +0200 Subject: [PATCH] Modify rule S3260: Update rule to include file access modifier (#1798) --- rules/S3260/csharp/code.adoc | 57 ++++++++++++++++++++++++++ rules/S3260/csharp/how.adoc | 3 ++ rules/S3260/csharp/impact.adoc | 67 +++++++++++++++++++++++++++++++ rules/S3260/csharp/resources.adoc | 10 +++++ rules/S3260/csharp/rule.adoc | 37 +++-------------- rules/S3260/csharp/why.adoc | 3 ++ 6 files changed, 145 insertions(+), 32 deletions(-) create mode 100644 rules/S3260/csharp/code.adoc create mode 100644 rules/S3260/csharp/how.adoc create mode 100644 rules/S3260/csharp/impact.adoc create mode 100644 rules/S3260/csharp/resources.adoc create mode 100644 rules/S3260/csharp/why.adoc diff --git a/rules/S3260/csharp/code.adoc b/rules/S3260/csharp/code.adoc new file mode 100644 index 0000000000..b6f18a4831 --- /dev/null +++ b/rules/S3260/csharp/code.adoc @@ -0,0 +1,57 @@ +=== Code examples + +==== Noncompliant code example + +[source,csharp,diff-id=1,diff-type=noncompliant] +---- +private class MyClass // Noncompliant +{ + // ... +} + +private record MyRecord // Noncompliant +{ + // ... +} +---- + +[source,csharp,diff-id=2,diff-type=noncompliant] +---- +file class MyClass // Noncompliant +{ + // ... +} + +file record MyRecord // Noncompliant +{ + // ... +} +---- + +==== Compliant solution + +[source,csharp,diff-id=1,diff-type=compliant] +---- +private sealed class MyClass +{ + // ... +} + +private sealed record MyRecord +{ + // ... +} +---- + +[source,csharp,diff-id=2,diff-type=compliant] +---- +file sealed class MyClass +{ + // ... +} + +file sealed record MyRecord +{ + // ... +} +---- \ No newline at end of file diff --git a/rules/S3260/csharp/how.adoc b/rules/S3260/csharp/how.adoc new file mode 100644 index 0000000000..8538f4dbb2 --- /dev/null +++ b/rules/S3260/csharp/how.adoc @@ -0,0 +1,3 @@ +== How to fix it + +The code can be improved by adding the `sealed` keyword in front of the `class` or `record` types that have no inheritors. \ No newline at end of file diff --git a/rules/S3260/csharp/impact.adoc b/rules/S3260/csharp/impact.adoc new file mode 100644 index 0000000000..216ab08354 --- /dev/null +++ b/rules/S3260/csharp/impact.adoc @@ -0,0 +1,67 @@ +=== What is the potential impact? + +We measured at least 4x improvement in the execution time by running the following snippet with https://github.com/dotnet/BenchmarkDotNet[BenchmarkDotNet]. + +[source,csharp] +---- +[Params(1_000_000)] +public int Iterations { get; set; } + +private readonly UnsealedClass unsealedType = new UnsealedClass(); +private readonly SealedClass sealedType = new SealedClass(); + +[Benchmark(Baseline = true)] +public void UnsealedType() +{ + for (int i = 0; i < Iterations; i++) + { + unsealedType.DoNothing(); + } +} + +[Benchmark] +public void SealedType() +{ + for (int i = 0; i < Iterations; i++) + { + sealedType.DoNothing(); + } +} + +private class BaseType +{ + public virtual void DoNothing() { } +} + +private class UnsealedClass : BaseType +{ + public override void DoNothing() { } +} + +private sealed class SealedClass : BaseType +{ + public override void DoNothing() { } +} +---- + +[options="header"] +|=== +|Method | Runtime | Iterations | Mean | Error | StdDev | Ratio +| UnsealedType | .NET 5.0 | 1000000 | 918.7 us | 12.09 us | 10.72 us | 1.00 +| SealedType | .NET 5.0 | 1000000 | 231.2 us | 3.61 us | 3.20 us | 0.25 +| UnsealedType | .NET 6.0 | 1000000 | 867.9 us | 6.38 us | 5.65 us | 1.00 +| SealedType | .NET 6.0 | 1000000 | 218.4 us | 0.71 us | 0.59 us | 0.25 +| UnsealedType | .NET 7.0 | 1000000 | 1,074.5 us | 3.55 us | 3.15 us | 1.00 +| SealedType | .NET 7.0 | 1000000 | 216.1 us | 1.28 us | 1.19 us | 0.20 +|=== + +Configuration used for running the benchmarks: +``` +BenchmarkDotNet=v0.13.5, OS=Windows 10 (10.0.19045.2846/22H2/2022Update) +12th Gen Intel Core i7-12800H, 1 CPU, 20 logical and 14 physical cores +.NET SDK=7.0.203 + [Host] : .NET 7.0.5 (7.0.523.17405), X64 RyuJIT AVX2 + .NET 5.0 : .NET 5.0.17 (5.0.1722.21314), X64 RyuJIT AVX2 + .NET 6.0 : .NET 6.0.16 (6.0.1623.17311), X64 RyuJIT AVX2 + .NET 7.0 : .NET 7.0.5 (7.0.523.17405), X64 RyuJIT AVX2 +``` \ No newline at end of file diff --git a/rules/S3260/csharp/resources.adoc b/rules/S3260/csharp/resources.adoc new file mode 100644 index 0000000000..be31845d32 --- /dev/null +++ b/rules/S3260/csharp/resources.adoc @@ -0,0 +1,10 @@ +== Resources + +=== Documentation + +* https://learn.microsoft.com/en-us/dotnet/csharp/language-reference/keywords/sealed[The `sealed` keyword] + +=== Articles & blog posts + +* https://code-maze.com/improve-performance-sealed-classes-dotnet[Boosting Performance With Sealed Classes in .NET] +* https://devblogs.microsoft.com/dotnet/performance-improvements-in-net-6/#peanut-butter[Performance Improvements in .NET 6] \ No newline at end of file diff --git a/rules/S3260/csharp/rule.adoc b/rules/S3260/csharp/rule.adoc index 1f7a12c1be..385a6ec8af 100644 --- a/rules/S3260/csharp/rule.adoc +++ b/rules/S3260/csharp/rule.adoc @@ -1,39 +1,12 @@ -== Why is this an issue? +include::why.adoc[] -``++private++`` classes and records aren't visible outside of their assemblies anyway, so if they're not extended inside the assemblies, they should be made explicitly non-extensible with the addition of the ``++sealed++`` keyword. +include::impact.adoc[] +include::how.adoc[] -=== Noncompliant code example - -[source,csharp] ----- -private class MyClass // Noncompliant -{ - // ... -} - -private record MyRecord // Noncompliant -{ - // ... -} ----- - - -=== Compliant solution - -[source,csharp] ----- -private sealed class MyClass -{ - // ... -} - -private sealed record MyRecord -{ - // ... -} ----- +include::code.adoc[] +include::resources.adoc[] ifdef::env-github,rspecator-view[] diff --git a/rules/S3260/csharp/why.adoc b/rules/S3260/csharp/why.adoc new file mode 100644 index 0000000000..f119990ebc --- /dev/null +++ b/rules/S3260/csharp/why.adoc @@ -0,0 +1,3 @@ +== Why is this an issue? + +Classes and records with either `private` or `file` access modifiers aren't visible outside of their assemblies or files, so if they're not extended inside their scope, they should be made explicitly non-extensible with the addition of the `sealed` keyword. \ No newline at end of file