NDepend

Improve your .NET code quality with NDepend

Top 10 .NET 5.0 new APIs

When a new major .NET version hits Release Candidate, it is time to use the NDepend code review changes capabilities to browse which new APIs have been added.

It is pretty straightforward. Start VisualNDepend.exe ; click Compare 2 versions of a code base ; then choose all assemblies in both folders:

  • C:\Program Files\dotnet\shared\Microsoft.NETCore.App\5.0.0-preview.4.20251.6
  • C:\Program Files\dotnet\shared\Microsoft.NETCore.App\3.1.7
NDepend Compare .NET 5.0 with .NET Core 3.1.7 Dialog
NDepend Compare .NET 5.0 with .NET Core 3.1.7 Dialog

Click Ok and a minute later after analyzing both BCL versions you are ready to explore changes and new stuff. We prepared a code query to match new .NET 5.0 types.

.NET 5.0 new public types
.NET 5.0 new public types

129 types are matched. See them listed along with the code query details at the end of the post. This is less than usual. For example .NET Core 3.0 introduced 359 new types. .NET 5.0 is essentially a major move to:

Certainly the teams were busy achieving these goals. Thus they added less new APIs than usual. However there are still some new goodies APIs with .NET 5.0. Let’s explore them.

1) System.Collections.Generic.IReadOnlySet<T>

Regularly new types and new methods are added to the popular generic collections API. This time we get a new IReadOnlySet<T> interface that can make code more SOLID because:

  • Often when using a set, the client just have to check if some elements are in a set or not. This interface avoids clients to have access to Add/Remove methods. This is a good application of the Interface Segregation Principle (ISP)
  • A set implementation might be read only. Such class doesn’t have to implement the ISet<T> interface with Add/remove methods. Else it would throw an NotSupportedException and this would be a violation of the Liskov Substitution Principle (LSP). Interestingly enough Array implementing ICollection<T> is a major violation of LSP.
A dreaded Liskov Substitution Principle violation
A dreaded Liskov Substitution Principle violation

All set implementations now propose this interface.

Classes that implement IReadOnlySet<T>
Classes that implement IReadOnlySet<T>

Small Digression: IMHO the set implementation through a hash table (HashSet, Dictionary…) is one of the most powerful tool developers have to improve performance. Being able to check if a set contains an element or not in constant time (independently of the size of the set) is pretty close to magic. Moreover usual hash table implementations rely on prime numbers which is fun. NDepend is stuffed with algorithms rethoughted to depend on some hash sets at their hearts. Hash table constant time search comes at the price of higher memory consumption so it must be carefully tuned. But most of the time it is worth it.

2) System.Collections.Generic.ReferenceEqualityComparer

The new class ReferenceEqualityComparer implements IEqualityComparer<T>. It uses reference equality (ReferenceEquals(Object, Object)) instead of value equality (Equals(Object)) when comparing two objects. The question on StackOverflow asking about such class gathered only 8K views and 60 up in 10 years. This is a hint that this new class won’t be often useful (as suspected) but at least it fills a gap.

3) New System.Net.Connections API

[Edit: From this document it seems that this API is delayed to .NET 6.0. However it is still here in .NET 5.0 RC1 compiled assemblies.] 

This new System.Net.Connections API (not yet documented online at this time) is an abstraction for composable connection establishment. Its goal is to improve layering separation and provide a standard extensibility model for making network connections. For example:

can be transformed into:

The new API has its own assembly, here are the type:

System.Net.Connections API
System.Net.Connections API

You can find this API discussion on github here and a 2h16 hours video discussion on API design here. Krzysztof Cwalina participates. Krzysztof is a .NET API design expert that co-authored the famous Framework Design Guidelines: Conventions, Idioms, and Patterns for Reusable .NET Libraries. One of the most exciting book in the .NET early days which is still relevant on many points.

4) Improved System.Text.Json.Serialization API

New types have been added to the namespace System.Text.Json.Serialization. The new types are bold in the screenshot below (underlined types are the ones with modified implementation):

System.Text.Json.Serialization new APIs
System.Text.Json.Serialization new APIs

These new types can be used to handle these scenarios:

5) New System.Net.Http.Json extension methods

The new System.Net.Http.Json provides extension methods for HttpClient and HttpContent that perform automatic serialization and deserialization using System.Text.Json. Here is a Steve Gordon post illustrating usages of this new API.

New System.Net.Http.Json extension methods
New System.Net.Http.Json extension methods

6) WinRT Interop (Breaking Change)

From this github design note we can read:

“We are moving to a new model for supporting WinRT APIs as part of .NET 5.0. This includes calling APIs (in either direction; CLR <==> WinRT), marshaling of data between the two type systems, and unification of types that are intended to be single instance (i.e. “projected types”). We will be removing the existing WinRT interop system from the .NET runtime (and any other associated components) as part of .NET 5.0.”

Here are the new types. Their documentation is here.

New WinRT.Interop API
New WinRT.Interop API

7) New GetGCMemoryInfo API

The structure GCMemoryInfo obtains through GC.GetGCMemoryInfo() exposes new data shown in bold in the screenshot below:

New GC data
New GC data

There are 2 new GC data related types.

  • System.GCGenerationInfo: Represents the size and the fragmentation of a generation on entry and on exit of the GC reported in GCMemoryInfo.
  • System.GCKind: Specifies the kind of a garbage collection : Any, Background, Ephemeral (A gen0 or gen1 collection) or FullBlocking (A blocking gen2 collection.)

Noah Falk from MS proposes on github some programs to gather GC fragmentation info. The most likely use cases for using this new API are for logging/monitoring. Alternatively it can be used to anticipate GC operations. This can be useful for example to indicate to a loader balancer that a machine should be taken out of rotation to request a full GC. It could also be used to avoid container hard-limits by reducing the size of caches.

8) System.Half

float (System.Single) is a 32 bits floating number structure.

double (System.Double) is a 64 bits floating number structure.

The new System.Half is a 16 bits floating number structure that implements IEEE 754. The main advantage of Half is that its values are stored on 2 bytes only. Thus its primary use cases are to save on storage space where the computed result does not need to be stored with full precision. Machine learning, graphics cards, the latest processors, native SIMD libraries are example of technologies that can benefit from Half. More explanations and details on Half can be found in this Half introduction blog post.

9) .NET’s Activity API improved to enable all OpenTelemetry scenarios

Several new types have been added to System.Diagnostics to enable all OpenTelemetry scenarios.

New System.Diagnostics.Activity Types
New System.Diagnostics.Activity Types

There are few resources online at this time about this new API, here are some of them:

10) New CodeAnalysis API

.NET Core and .NET 5.0 supports self-contained deployment. The entire framework is bundled with the application. Doing so resolves some versioning hell. But also doing so relieves the Microsoft engineers from the dreaded ascendant compatibility constraint they had on the .NET Framework: .NET is now free to evolve without this constraint.

The downside of this approach is that the application size grows significantly because the BCL is quite huge. This is why Microsoft invests in trimming the framework: discard unused types and members in assemblies themselves. This necessity is even more essential for Blazor WebAssembly applications that download the framework in the browser at page load time. Related posts are:

Trimming is done through some static analyzers. However static analyzers (Roslyn analyzers, NDepend rules…) don’t execute the code. As a consequence they have a limited understanding of what really happens at runtime. Especially when some types are resolved dynamically, often through System.Reflection.

There are new types in the CodeAnalysis API. Most of them are needed to indicate to static analyzers when a type or a method resolves dynamically some code.

New CodeAnalysis Types
New CodeAnalysis Types
  • UnconditionalSuppressMessageAttribute: Suppresses reporting of a specific rule violation, allowing multiple suppressions on a single code artifact. It is different than SuppressMessageAttribute in that it doesn’t have a ConditionalAttribute. It’s always preserved in the compiled assembly.
  • RequiresUnreferencedCodeAttribute: This attribute can tag constructor and methods. It Indicates that the specified method requires dynamic access to code that is not referenced statically, for example, through System.Reflection. This attribute allows tools to understand which methods are unsafe to call when removing unreferenced code from an application.
  • DynamicallyAccessedMembersAttribute: This attribute can tag some members (fields, methods…). It indicates that certain members on a specified type are accessed dynamically, for example, through System.Reflection.
  • DynamicDependencyAttribute: This attribute can be used to inform tooling of a dependency that is otherwise not evident purely from metadata and IL, for example, a member relied on via reflection. It can tag constructors, methods and fields.
  • MemberNotNullAttribute: Specifies that the method or property tagged with this attribute, ensure that the listed field and property members have values that aren’t null.
  • MemberNotNullWhenAttribute: Specifies that the method or property tagged with this attribute, will ensure that the listed field and property members have non-null values when returning with the specified return value condition.

These types are quite interesting for anyone interested in static analysis. We take note to support them in NDepend.

11) (bonus) System.Threading.Tasks.TaskCompletionSource

The new class System.Threading.Tasks.TaskCompletionSource is to System.Threading.Tasks.Task, what System.Threading.Tasks.TaskCompletionSource<Result> (that was already there in .NET Core 3) is to System.Threading.Tasks.Task<Result>: the producer side of a Task unbound to a delegate, providing access to the consumer side through the Task property. See some discussion on this class in this stackoverflow Q/A: When should TaskCompletionSource<T> be used?

The list of new .NET 5.0 public types

To list all new types we had to refine the default New Types code query because:

  • We want only public API types
  • Some types got moved from one assembly to another. In this situation NDepend sees a removed and a new type. Hence we use the hashset olderTypNames to avoid matched those.
  • Some type definitions are duplicated within the assembly System.Private.CoreLib. We use the newTypesNamesLookup to avoid matching those twice.

Here are the 129 types matched:

129 types # IL instructions AsmName Full Name
JsonSerializerDefaults N/A System.Text.Json System.Text.Json.JsonSerializerDefaults
JsonConstructorAttribute 3 System.Text.Json System.Text.Json.Serialization .JsonConstructorAttribute
JsonIncludeAttribute 3 System.Text.Json System.Text.Json.Serialization .JsonIncludeAttribute
JsonNumberHandlingAttribute 15 System.Text.Json System.Text.Json.Serialization .JsonNumberHandlingAttribute
JsonIgnoreCondition N/A System.Text.Json System.Text.Json.Serialization .JsonIgnoreCondition
JsonNumberHandling N/A System.Text.Json System.Text.Json.Serialization .JsonNumberHandling
ReferenceHandler 11 System.Text.Json System.Text.Json.Serialization .ReferenceHandler
ReferenceHandler<T> 6 System.Text.Json System.Text.Json.Serialization .ReferenceHandler<T>
ReferenceResolver 3 System.Text.Json System.Text.Json.Serialization .ReferenceResolver
X509ChainTrustMode N/A System.Security.Cryptography
.X509Certificates
System.Security.Cryptography .X509Certificates.X509ChainTrustMode
PemFields 25 System.Security.Cryptography
.Encoding
System.Security.Cryptography.PemFields
PemEncoding 810 System.Security.Cryptography
.Encoding
System.Security.Cryptography.PemEncoding
DSASignatureFormat N/A System.Security.Cryptography
.Algorithms
System.Security.Cryptography .DSASignatureFormat
HKDF 376 System.Security.Cryptography
.Algorithms
System.Security.Cryptography.HKDF
AdvSimd 3 226 System.Runtime.Intrinsics System.Runtime.Intrinsics.Arm.AdvSimd
Aes 18 System.Runtime.Intrinsics System.Runtime.Intrinsics.Arm.Aes
ArmBase 10 System.Runtime.Intrinsics System.Runtime.Intrinsics.Arm.ArmBase
Crc32 14 System.Runtime.Intrinsics System.Runtime.Intrinsics.Arm.Crc32
Dp 26 System.Runtime.Intrinsics System.Runtime.Intrinsics.Arm.Dp
Rdm 50 System.Runtime.Intrinsics System.Runtime.Intrinsics.Arm.Rdm
Sha1 14 System.Runtime.Intrinsics System.Runtime.Intrinsics.Arm.Sha1
Sha256 10 System.Runtime.Intrinsics System.Runtime.Intrinsics.Arm.Sha256
X86Base 38 System.Runtime.Intrinsics System.Runtime.Intrinsics.X86.X86Base
CreateComInterfaceFlags N/A System.Runtime.InteropServices System.Runtime.InteropServices .CreateComInterfaceFlags
CreateObjectFlags N/A System.Runtime.InteropServices System.Runtime.InteropServices .CreateObjectFlags
ComWrappers 198 System.Runtime.InteropServices System.Runtime.InteropServices .ComWrappers
CollectionsMarshal 13 System.Runtime.InteropServices System.Runtime.InteropServices .CollectionsMarshal
IDynamicInterfaceCastable N/A System.Runtime.InteropServices System.Runtime.InteropServices .IDynamicInterfaceCastable
DynamicInterfaceCastable
ImplementationAttribute
3 System.Runtime.InteropServices System.Runtime.InteropServices .DynamicInterfaceCastable
ImplementationAttribute
UnmanagedCallersOnly
Attribute
3 System.Runtime.InteropServices System.Runtime.InteropServices .UnmanagedCallersOnlyAttribute
GCGenerationInfo 12 System.Runtime System.GCGenerationInfo
GCKind N/A System.Runtime System.GCKind
Half 978 System.Runtime System.Half
TaskCompletionSource 166 System.Runtime System.Threading.Tasks .TaskCompletionSource
OSPlatformAttribute 9 System.Runtime System.Runtime.Versioning .OSPlatformAttribute
TargetPlatformAttribute 4 System.Runtime System.Runtime.Versioning .TargetPlatformAttribute
SupportedOSPlatform
Attribute
4 System.Runtime System.Runtime.Versioning .SupportedOSPlatformAttribute
UnsupportedOSPlatform
Attribute
4 System.Runtime System.Runtime.Versioning .UnsupportedOSPlatformAttribute
SuppressGCTransitionAttribute 3 System.Runtime System.Runtime.InteropServices .SuppressGCTransitionAttribute
IsExternalInit 0 System.Runtime System.Runtime.CompilerServices .IsExternalInit
ModuleInitializerAttribute 3 System.Runtime System.Runtime.CompilerServices .ModuleInitializerAttribute
PreserveBaseOverridesAttribute 3 System.Runtime System.Runtime.CompilerServices .PreserveBaseOverridesAttribute
SkipLocalsInitAttribute 3 System.Runtime System.Runtime.CompilerServices .SkipLocalsInitAttribute
RequiresUnreferenced
CodeAttribute
16 System.Runtime System.Diagnostics.CodeAnalysis .RequiresUnreferencedCodeAttribute
DynamicallyAccessed
MemberTypes
N/A System.Runtime System.Diagnostics.CodeAnalysis .DynamicallyAccessedMemberTypes
DynamicallyAccessed
MembersAttribute
9 System.Runtime System.Diagnostics.CodeAnalysis .DynamicallyAccessedMembersAttribute
DynamicDependencyAttribute 70 System.Runtime System.Diagnostics.CodeAnalysis .DynamicDependencyAttribute
MemberNotNullAttribute 20 System.Runtime System.Diagnostics.CodeAnalysis .MemberNotNullAttribute
MemberNotNullWhenAttribute 29 System.Runtime System.Diagnostics.CodeAnalysis .MemberNotNullWhenAttribute
UnconditionalSuppress
MessageAttribute
43 System.Runtime System.Diagnostics.CodeAnalysis .UnconditionalSuppressMessageAttribute
IReadOnlySet<T> N/A System.Runtime System.Collections.Generic.IReadOnlySet <T>
CppInlineNamespace
Attribute
3 System.Runtime
.CompilerServices.VisualC
System.Runtime.CompilerServices .CppInlineNamespaceAttribute
AdvSimd+Arm64 1 180 System.Private.CoreLib System.Runtime.Intrinsics.Arm .AdvSimd+Arm64
Aes+Arm64 2 System.Private.CoreLib System.Runtime.Intrinsics.Arm.Aes+Arm64
ArmBase+Arm64 14 System.Private.CoreLib System.Runtime.Intrinsics.Arm .ArmBase+Arm64
Crc32+Arm64 6 System.Private.CoreLib System.Runtime.Intrinsics.Arm .Crc32+Arm64
Dp+Arm64 2 System.Private.CoreLib System.Runtime.Intrinsics.Arm.Dp+Arm64
Rdm+Arm64 26 System.Private.CoreLib System.Runtime.Intrinsics.Arm.Rdm+Arm64
Sha1+Arm64 2 System.Private.CoreLib System.Runtime.Intrinsics.Arm.Sha1+Arm64
Sha256+Arm64 2 System.Private.CoreLib System.Runtime.Intrinsics.Arm .Sha256+Arm64
X86Base+X64 8 System.Private.CoreLib System.Runtime.Intrinsics.X86 .X86Base+X64
Aes+X64 2 System.Private.CoreLib System.Runtime.Intrinsics.X86.Aes+X64
Avx+X64 2 System.Private.CoreLib System.Runtime.Intrinsics.X86.Avx+X64
Avx2+X64 2 System.Private.CoreLib System.Runtime.Intrinsics.X86.Avx2+X64
Fma+X64 2 System.Private.CoreLib System.Runtime.Intrinsics.X86.Fma+X64
Pclmulqdq+X64 2 System.Private.CoreLib System.Runtime.Intrinsics.X86 .Pclmulqdq+X64
Sse3+X64 2 System.Private.CoreLib System.Runtime.Intrinsics.X86.Sse3+X64
Ssse3+X64 2 System.Private.CoreLib System.Runtime.Intrinsics.X86.Ssse3+X64
ComWrappers
+ComInterfaceEntry
0 System.Private.CoreLib System.Runtime.InteropServices .ComWrappers+ComInterfaceEntry
ComWrappers
+ComInterfaceDispatch
13 System.Private.CoreLib System.Runtime.InteropServices .ComWrappers+ComInterfaceDispatch
SslClientHelloInfo 13 System.Net.Security System.Net.Security.SslClientHelloInfo
ServerOptionsSelection
Callback
0 System.Net.Security System.Net.Security .ServerOptionsSelection
Callback
SslStreamCertificateContext 362 System.Net.Security System.Net.Security .SslStreamCertificateContext
NetworkException 68 System.Net.Primitives System.Net.NetworkException
NetworkError N/A System.Net.Primitives System.Net.NetworkError
HttpClientJsonExtensions 511 System.Net.Http.Json System.Net.Http.Json .HttpClientJsonExtensions
HttpContentJsonExtensions 422 System.Net.Http.Json System.Net.Http.Json .HttpContentJsonExtensions
JsonContent 465 System.Net.Http.Json System.Net.Http.Json.JsonContent
HeaderEncodingSelector
<TContext>
0 System.Net.Http System.Net.Http.HeaderEncodingSelector <TContext>
HttpRequestOptions 122 System.Net.Http System.Net.Http.HttpRequestOptions
HttpRequestOptionsKey
<TValue>
7 System.Net.Http System.Net.Http.HttpRequestOptionsKey <TValue>
HttpVersionPolicy N/A System.Net.Http System.Net.Http.HttpVersionPolicy
HttpKeepAlivePingPolicy N/A System.Net.Http System.Net.Http.HttpKeepAlivePingPolicy
ConnectionBase 134 System.Net.Connections System.Net.Connections.ConnectionBase
ConnectionCloseMethod N/A System.Net.Connections System.Net.Connections .ConnectionCloseMethod
ConnectionExtensions 40 System.Net.Connections System.Net.Connections .ConnectionExtensions
ConnectionListenerFactory 106 System.Net.Connections System.Net.Connections .ConnectionListenerFactory
Connection 135 System.Net.Connections System.Net.Connections.Connection
ConnectionFactory 106 System.Net.Connections System.Net.Connections.ConnectionFactory
ConnectionListener 106 System.Net.Connections System.Net.Connections .ConnectionListener
IConnectionProperties N/A System.Net.Connections System.Net.Connections .IConnectionProperties
SocketsConnectionFactory 187 System.Net.Connections System.Net.Connections .SocketsConnectionFactory
AnonymousPipeServer
StreamAcl
6 System.IO.Pipes System.IO.Pipes .AnonymousPipeServerStreamAcl
NamedPipeServerStreamAcl 12 System.IO.Pipes System.IO.Pipes.NamedPipeServerStreamAcl
AnonymousPipeServer
StreamAcl
6 System.IO.Pipes.AccessControl System.IO.Pipes .AnonymousPipeServer
StreamAcl
NamedPipeServer
StreamAcl
12 System.IO.Pipes.AccessControl System.IO.Pipes.NamedPipeServerStreamAcl
FlushResult 34 System.IO.Pipelines System.IO.Pipelines.FlushResult
IDuplexPipe N/A System.IO.Pipelines System.IO.Pipelines.IDuplexPipe
Pipe 1 642 System.IO.Pipelines System.IO.Pipelines.Pipe
PipeOptions 107 System.IO.Pipelines System.IO.Pipelines.PipeOptions
PipeReader 422 System.IO.Pipelines System.IO.Pipelines.PipeReader
PipeScheduler 17 System.IO.Pipelines System.IO.Pipelines.PipeScheduler
PipeWriter 218 System.IO.Pipelines System.IO.Pipelines.PipeWriter
ReadResult 40 System.IO.Pipelines System.IO.Pipelines.ReadResult
StreamPipeExtensions 21 System.IO.Pipelines System.IO.Pipelines.StreamPipeExtensions
StreamPipeReaderOptions 60 System.IO.Pipelines System.IO.Pipelines .StreamPipeReaderOptions
StreamPipeWriterOptions 42 System.IO.Pipelines System.IO.Pipelines .StreamPipeWriterOptions
Asn1Tag 495 System.Formats.Asn1 System.Formats.Asn1.Asn1Tag
AsnContentException 26 System.Formats.Asn1 System.Formats.Asn1.AsnContentException
AsnEncodingRules N/A System.Formats.Asn1 System.Formats.Asn1.AsnEncodingRules
AsnDecoder 4 410 System.Formats.Asn1 System.Formats.Asn1.AsnDecoder
AsnReader 696 System.Formats.Asn1 System.Formats.Asn1.AsnReader
AsnReaderOptions 29 System.Formats.Asn1 System.Formats.Asn1.AsnReaderOptions
AsnWriter 2 922 System.Formats.Asn1 System.Formats.Asn1.AsnWriter
AsnWriter+Scope 90 System.Formats.Asn1 System.Formats.Asn1.AsnWriter+Scope
TagClass N/A System.Formats.Asn1 System.Formats.Asn1.TagClass
UniversalTagNumber N/A System.Formats.Asn1 System.Formats.Asn1.UniversalTagNumber
ActivityTagsCollection 419 System.Diagnostics
.DiagnosticSource
System.Diagnostics .ActivityTagsCollection
ActivityTagsCollection
+Enumerator
27 System.Diagnostics
.DiagnosticSource
System.Diagnostics .ActivityTagsCollection+Enumerator
ActivityContext 119 System.Diagnostics
.DiagnosticSource
System.Diagnostics.ActivityContext
ActivityCreationOptions<T> 116 System.Diagnostics
.DiagnosticSource
System.Diagnostics .ActivityCreationOptions<T>
ActivitySamplingResult N/A System.Diagnostics
.DiagnosticSource
System.Diagnostics .ActivitySamplingResult
ActivityEvent 44 System.Diagnostics
.DiagnosticSource
System.Diagnostics.ActivityEvent
ActivityKind N/A System.Diagnostics
.DiagnosticSource
System.Diagnostics.ActivityKind
ActivityLink 86 System.Diagnostics
.DiagnosticSource
System.Diagnostics.ActivityLink
SampleActivity<T> 0 System.Diagnostics
.DiagnosticSource
System.Diagnostics.SampleActivity<T>
ActivityListener 41 System.Diagnostics
.DiagnosticSource
System.Diagnostics.ActivityListener
ActivitySource 501 System.Diagnostics
.DiagnosticSource
System.Diagnostics.ActivitySource
ReferenceEqualityComparer 15 System.Collections System.Collections.Generic .ReferenceEqualityComparer

My dad being an early programmer in the 70's, I have been fortunate to switch from playing with Lego, to program my own micro-games, when I was still a kid. Since then I never stop programming.

I graduated in Mathematics and Software engineering. After a decade of C++ programming and consultancy, I got interested in the brand new .NET platform in 2002. I had the chance to write the best-seller book (in French) on .NET and C#, published by O'Reilly (> 15.000 copies) and also did manage some academic and professional courses on the platform and C#.

Over the years, I gained a passion for understanding structure and evolution of large complex real-world applications, and for talking with talented developers behind it. As a consequence, I got interested in static code analysis and started the project NDepend.

Today, with more than 8.000 client companies, including many of the Fortune 500 ones, NDepend offers deeper insight and understanding about their code bases to a wide range of professional users around the world.

I live with my wife and our twin babies Léna and Paul, in the beautiful island of Mauritius in the Indian Ocean.

Comments:

Leave a Reply

Your email address will not be published. Required fields are marked *