This is part 11/17 of my Exploring the .NET CoreFX series.
In 2008, Microsoft Research published Code Contracts, which provide a language-agnostic way to express coding assumptions in .NET programs. The assumptions take the form of pre-conditions, post-conditions, and object invariants.
Here is a simple example of code which uses Code Contracts:
Code Contracts assertions are not limited to runtime enforcement. They may instead be enforced by compile-time static analysis. For example, it is very simple to annotate methods with Code Contracts, set up a continuous integration (CI) server to perform static analysis, and fail the build if there are any failed assertions. This gives us the best of both worlds: a guarantee our code enforces our assumptions with essentially zero runtime penalty.
This is part 10/17 of my Exploring the .NET CoreFX series.
The .NET Core’s System.Collections.Immutable.ImmutableArray provides two enumerators. The first has been highly tuned for speed, and the second is a fallback for compatibility when it is required.
The high-performance enumerator uses the following performance optimizations:
The enumerator is a struct, rather than a class, so that it is stack-allocated rather than heap-allocated.
The enumerator does not implement IEnumerator or IEnumerator, as this would require it to implement IDisposable. By not implementing IDisposable the iterator will inline during foreach loops..
The enumerator does not use range checks in Enumerator.Current; it requires on .NET’s array range checks to throw an exception instead.
The high-performance enumerator is called ImmutableArray.Enumerator, which is returned by ImmutableArray.GetEnumerator():
The .NET Core’s immutable collections assembly, System.Collections.Immutable, also uses the builder pattern, but for a slightly different reason: to improve the performance of making many changes to the collection. This is possible because, unlike the immutable collection itself, the builder pattern does not need to maintain the immutable collection’s invariants after each modification. The builder pattern merely needs to reestablish the invariants of the immutable collection upon the publishing of the results.
This is part 8/17 of my Exploring the .NET CoreFX series.
The .NET Core’s System.Collections.Immutable.ImmutableArray class implements an immutable wrapper around a normal C# managed array. This looks something like:
Within the ImmutableArray class, there are a number of methods which have the precondition that ImmutableArray.array must be initialized. These preconditions must be checked before the method begins processing to make sure we handle invalid states correctly.
This is part 7/17 of my Exploring the .NET CoreFX series.
In the previous post, I referenced EqualityComparer.Default. If T does not implement IEquatable, EqualityComparer.Default will use the framework-defined Object.Equals(), which implements reference equality.
However, many times you want to compare two types for structural equality (i.e. identical content) rather than reference equality (i.e. two references point to the same instance of the class). The interface IStructuralEquatable was defined to allow a class to explicitly implement structural, rather than reference equality. Related classes include IStructuralComparable and StructuralComparisons.
This is part 5/17 of my Exploring the .NET CoreFX series.
This is a simple recommendation based on observations from System.Collections.Immutable.
Recommendations
Keep the implementation of an indexer as trivial as possible to allow the JIT optimization of removing array bounds checking to work. For example, don’t check if a member variable is null; just use it and allow the NullReferenceException to happen naturally. In other words, use:
1
2
3
4
5
6
7
public T this[int index]
{
get {
returnthis.array[index];
}
}
This is part 3/17 of my Exploring the .NET CoreFX series.
System.Collections.Immutable uses a number of attributes to make it more debugger-friendly. Here are the key attributes:
DebuggerStepThrough
Occasionally a method is so simple that it doesn’t make sense to have the debugger step into it. The System.Diagnostics.DebuggerStepThroughAttribute instructs the debugger to step through the code instead of stepping into the code.
Here is an example from System.Collections.Immutable:
Unfortunately, thread-local storage comes at a cost. Reading or writing a thread-local variable is far more expensive than reading or writing a local variable. System.Collections.Immutable uses a trick or two to help ameliorate this expense. For example, System.Collections.Immutable caches thread-local variables in local variables in a method to avoid unnecessary TLS hits on repeated access. Here’s some sample code which implements this: