RJCP.Core.SysCompat
0.2.0
dotnet add package RJCP.Core.SysCompat --version 0.2.0
NuGet\Install-Package RJCP.Core.SysCompat -Version 0.2.0
<PackageReference Include="RJCP.Core.SysCompat" Version="0.2.0" />
paket add RJCP.Core.SysCompat --version 0.2.0
#r "nuget: RJCP.Core.SysCompat, 0.2.0"
// Install RJCP.Core.SysCompat as a Cake Addin #addin nuget:?package=RJCP.Core.SysCompat&version=0.2.0 // Install RJCP.Core.SysCompat as a Cake Tool #tool nuget:?package=RJCP.Core.SysCompat&version=0.2.0
RJCP.Core.SysCompat
This library provides implementation details that allow some backward compatibility from newer .NET Core features to older .NET Framework libraries when using newer C# Language versions.
- 1. .NET Framework Usage
- 2. Extensions
- 3. C# Language Features
- 4. Performance Metrics
- 5. Version History
1. .NET Framework Usage
To use this library with newer C# compiler versions, extend this in your .NET SDK project file:
1.1. Conditional Version Upgrade
By default the highest version of .NET Framework is C# 7.3. Newer compilers can still target older frameworks.
<?xml version="1.0" encoding="utf-8"?>
<Project Sdk="Microsoft.NET.Sdk">
<PropertyGroup>
<TargetFrameworks>net40;net6.0;net8.0</TargetFrameworks>
...
<LangVersion Condition="'$(TargetFrameworkIdentifier)' == '.NETFramework'">10</LangVersion>
</PropertyGroup>
</Project>
1.2. NuGet Package Reference
Add to your project a reference to RJCP.Core.SysCompat
to your project for
.NET Core and .NET Framework.
2. Extensions
2.1. CallerArgumentExpression
LangVersion | 10 |
Framework | .NET Framework |
C# 10 and later support the attribute CallerArgumentExpressionAttribute
that
is provided in .NET 6.0 and later. On .NET Framework, this package provides the
attribute, that the compiler can inject code. Ensure the LangVersion
is 10 or
later.
It is now possible to provide helper methods that can use the name of an argument and pass it on as further arguments.
For details on how this is used, please study the implementation of
ThrowHelper.ThrowIfNull(obj)
.
2.2. Throw Helpers
LangVersion | 7.3 |
Framework | .NET Framework and Core |
To resolve Code Analysis findings (e.g. CA1510, CA1511, CA1512, CA1513 and among others), helper methods have been created that can be used on older frameworks.
Notes:
- ¹ These methods have overloads for the basic types
int
,long
,nint
,float
,double
,uint
,ulong
andnuint
. The .NET implementation is slightly different taking generic types not available in older frameworks. Of course, for some methods where it doesn't make sense (throw if not negative or zero), only methods with signed types are provided. - ² Provides an interface for
IEquatable<T>
. These use the default equatable for the type, and so thevalue
may benull
when comparing, in addition to theother
parameter. This is how the .NET implementation also implements the method. - ³ Provides an interface for
IComparable<T>
. These use the default comparer for the type, and so thevalue
may benull
when comparing, in addition to theother
parameter. This is different to the .NET implementation.
The Package is compiled with the language version C# 10, so that projects using this library for this feature do not need to upgrade.
The additional methods are provided, which are not part of the framework. These
methods are useful if you have your own translated strings that you wish to
provide the user. Note that the method name must change, due to the overload
ThrowIfNullOrEmpty(string, string)
exists.
using System;
ThrowHelper.ThrowIfNullOrEmptyMsg(message, strparam);
ThrowHelper.ThrowIfNullOrWhiteSpace(message, strparam);
2.2.1. How Throw Helpers get the Argument Name
C# 10 and later support the attribute CallerArgumentExpressionAttribute
that
is provided in .NET 6.0 and later. On .NET Framework, it uses the attribute
provided by this package, and the Roslyn compiler will still inject the
necessary code to provide the attribute name.
public static void ThrowIfNull(object argument,
[CallerArgumentExpression(nameof(argument))] string paramName = null)
2.3. SupportedOSPlatform
LangVersion | 7.3 |
Framework | .NET Framework |
With .NET 6.0 and later, code analysis warning CA1416 Call Site Reachable can significantly support identifying potential failures in multi-target code.
To avoid unnecessary conditional compiles, it is convenient to define the
SupportedOSPlatform
attribute, that the same code can compile for .NET Framework.
3. C# Language Features
3.1. Init Properties
LangVersion | 9 |
Framework | .NET Framework and Core |
It is not recommended enabling this for .NET Framework for reusable NuGet packages.
3.1.1. Feature Description
C# 9 introduces two new keywords init. Properties are initialised optionally. The benefit is to define properties that are initialised at the same time the constructor is called, such that the properties are set without having to set them within the constructor.
For example, instead of:
public class MyClass {
public MyClass(string prop) {
Prop = prop;
}
public string Prop { get; }
}
MyClass c = new("value");
we can instead have:
public class MyClass {
public string Prop { get; init; }
}
MyClass c = new() {
Prop = "value"
};
Benefit is more obvious with more properties (e.g. 3 or more), that values are defined without having to have a lot of arguments in constructors, or variations of parameters in the constructor.
3.1.2. Enablement
The RJCP.Core.SysCompat
library unfortunately cannot help, which is why the
feature is described here. In your assembly for .NET Framework, ensure that:
The language version is set to 9 or greater (.NET Core 5 or later) in both the library defining the
init
property, and in the executable consuming the class with theinit
property.<PropertyGroup> <TargetFrameworks>net40;net462;net6.0;net8.0</TargetFrameworks> ... <LangVersion Condition="$(TargetFramework.StartsWith('net4'))">10</LangVersion>
Add the following class, compiled only for .NET Framework (or .NET 2.1 or earlier also). Putting this into the
RJCP.Core.SysCompat.dll
library does not work. This should be in the library defining theinit
property (not needed in the executable consuming the class with theinit
property).namespace System.Runtime.CompilerServices { using System.ComponentModel; #if NETFRAMEWORK || !NET5_0_OR_GREATER [EditorBrowsable(EditorBrowsableState.Never)] internal static class IsExternalInit { } #endif }
3.2. Required Properties
LangVersion | 11 |
Framework | .NET Framework and Core |
Along with init, C# 11 adds the keyword required, which enforces that initialised properties must be provided.
3.2.1. Enablement
If compiling with C# 10 or earlier:
Severity | Code | Description |
---|---|---|
Error | CS8936 | Feature 'required members' is not available in C# 10.0. Please use language version 11.0 or greater. |
Error | CS0656 | Missing compiler required member 'System.Runtime.CompilerServices.CompilerFeatureRequiredAttribute..ctor' |
Error | CS0656 | Missing compiler required member 'System.Runtime.CompilerServices.RequiredMemberAttribute..ctor' |
Set the language version to 11 or greater (.NET Core 7 or later) in both the library defining the
required
property, and in the executable consuming the class with therequired
property.<PropertyGroup> <TargetFrameworks>net40;net462;net6.0;net8.0</TargetFrameworks> ... <LangVersion>11</LangVersion>
Add the following class (not needed in the executable consuming the class with the
required
property)namespace System.Runtime.CompilerServices { using System.ComponentModel; #if NETFRAMEWORK || !NET5_0_OR_GREATER [EditorBrowsable(EditorBrowsableState.Never)] internal static class IsExternalInit { } #endif #if NETFRAMEWORK || !NET7_0_OR_GREATER [AttributeUsage(AttributeTargets.Class | AttributeTargets.Struct | AttributeTargets.Field | AttributeTargets.Property, AllowMultiple = false, Inherited = false)] internal sealed class RequiredMemberAttribute : Attribute { } [AttributeUsage(AttributeTargets.All, AllowMultiple = true, Inherited = false)] internal sealed class CompilerFeatureRequiredAttribute : Attribute { public CompilerFeatureRequiredAttribute(string featureName) { FeatureName = featureName; } public string FeatureName { get; } public bool IsOptional { get; init; } public const string RefStructs = nameof(RefStructs); public const string RequiredMembers = nameof(RequiredMembers); } #endif }
4. Performance Metrics
Results = net48
BenchmarkDotNet=v0.13.12 OS=Windows 10 (10.0.19045.4046/22H2/2022Update)
Intel Core i7-6700T CPU 2.80GHz (Skylake), 1 CPU(s), 8 logical and 4 physical core(s)
[HOST] : .NET Framework 4.8.1 (4.8.9181.0), X64 RyuJIT
Results = net6
BenchmarkDotNet=v0.13.12 OS=Windows 10 (10.0.19045.4046/22H2/2022Update)
Intel Core i7-6700T CPU 2.80GHz (Skylake), 1 CPU(s), 8 logical and 4 physical core(s)
[HOST] : .NET 6.0.27 (6.0.2724.6912), X64 RyuJIT
Results = net8
BenchmarkDotNet=v0.13.12 OS=Windows 10 (10.0.19045.4046/22H2/2022Update)
Intel Core i7-6700T CPU 2.80GHz (Skylake), 1 CPU(s), 8 logical and 4 physical core(s)
[HOST] : .NET 8.0.1 (8.0.123.58001), X64 RyuJIT
Project 'syscompat' Type | Method | mean (net48) | stderr | mean (net6) | stderr | mean (net8) | stderr |
---|---|---|---|---|---|---|---|
ThrowIfArray | ThrowIfArrayOutOfBounds | 3.03 | 0.01 | 3.40 | 0.01 | 3.39 | 0.01 |
ThrowIfEnum | ThrowIfEnumHasFlag | 23.25 | 0.01 | 1.38 | 0.01 | 1.15 | 0.00 |
ThrowIfEnum | ThrowIfEnumUndefined | 244.13 | 0.25 | 111.84 | 0.07 | 24.06 | 0.05 |
ThrowIfNullBenchmark | ThrowIfNull | 0.30 | 0.00 | 0.00 | 0.00 | 0.00 | 0.00 |
ThrowIfNullBenchmark | ThrowIfNull_System | - | - | 0.00 | 0.00 | 0.00 | 0.00 |
ThrowIfNullBenchmark | ThrowIfNullOrWhiteSpace | 4.10 | 0.01 | 3.46 | 0.01 | 1.67 | 0.00 |
ThrowIfNullBenchmark | ThrowIfNullOrWhiteSpace_System | - | - | - | - | 1.58 | 0.00 |
ThrowIfNullBenchmark | ThrowIfZero | 0.01 | 0.00 | 0.00 | 0.00 | 0.00 | 0.00 |
ThrowIfNullBenchmark | ThrowIfZero_System | - | - | - | - | 0.00 | 0.00 |
5. Version History
5.1. Version 0.2.0
- Initial Version
Product | Versions Compatible and additional computed target framework versions. |
---|---|
.NET | net6.0 is compatible. net6.0-android was computed. net6.0-ios was computed. net6.0-maccatalyst was computed. net6.0-macos was computed. net6.0-tvos was computed. net6.0-windows was computed. net7.0 was computed. net7.0-android was computed. net7.0-ios was computed. net7.0-maccatalyst was computed. net7.0-macos was computed. net7.0-tvos was computed. net7.0-windows was computed. net8.0 is compatible. net8.0-android was computed. net8.0-browser was computed. net8.0-ios was computed. net8.0-maccatalyst was computed. net8.0-macos was computed. net8.0-tvos was computed. net8.0-windows was computed. |
.NET Framework | net40 is compatible. net403 was computed. net45 was computed. net451 was computed. net452 was computed. net46 was computed. net461 was computed. net462 is compatible. net463 was computed. net47 was computed. net471 was computed. net472 was computed. net48 was computed. net481 was computed. |
-
.NETFramework 4.0
- No dependencies.
-
.NETFramework 4.6.2
- No dependencies.
-
net6.0
- No dependencies.
-
net8.0
- No dependencies.
NuGet packages (14)
Showing the top 5 NuGet packages that depend on RJCP.Core.SysCompat:
Package | Downloads |
---|---|
RJCP.Core.Environment
Provide extensions for environment functions. |
|
RJCP.SerialPortStream
An independent implementation of System.IO.Ports.SerialPort and SerialStream for better reliability and maintainability. |
|
RJCP.Diagnostics.Trace
Support and extend Logging for .NET Framework and .NET Core. |
|
RJCP.IO.Device
Get Windows Device Information. |
|
RJCP.IO.Buffer
Provides functionality for buffered I/O. |
GitHub repositories
This package is not used by any popular GitHub repositories.
Version | Downloads | Last updated |
---|---|---|
0.2.0 | 11,025 | 3/9/2024 |