I'm gonna start posting about what I learn going through .NET libraries and codebases. Each session, I will post about what I've learned, my insights, and my questions
C#/.NET being pushed hard running infrastructure at Ethereum's scale.
Nethermind runs on ~3k nodes and accounts for ~33% of staked nodes - supporting a ~$334B native asset, ~$146B of stablecoins and ~$54B deposited in DeFi apps.
Nethermind 2.0 has some amazing performance work in it: up to 2.55x block throughput, p99 block processing from 1.65s to 208ms, eth_call RPC 15-27% faster and Keccak-256 ~30% faster with platform intrinsics.
@valigo I am sure splitting this abomination of a class would be a net benefit. No reason whatsoever to put everything into one class. This is C# and Roslyn and the JIT compiler will optimize at the method level not class level
.NET Internals
Session 3
A note on the heavy use of extension methods in the .NET codebase.
IServiceCollection, like many other .NET abstractions, is heavily extended through extension methods.
There are reasons for this that are actually part of Microsoft's own framework design guidelines, and I would like to explore them because I find them very cool and useful.
We already see this pattern everywhere in familiar APIs such as IEnumerable<T> and LINQ.
The most important guideline for me is this:
I'm gonna start posting about what I learn going through .NET libraries and codebases. Each session, I will post about what I've learned, my insights, and my questions
And suddenly:
services.AddSomething();
looks and feels like part of IServiceCollection, even though the interface itself was never modified.
That gives us a very interesting combination:
Open extension
Stable contract
The interface can stay small and stable as long as its primitive operations are sufficient to express the higher-level functionality.
New functionality can then continue to grow around it through extension methods without forcing every implementation of the interface to change.
.NET Internals
Session 3
A note on the heavy use of extension methods in the .NET codebase.
IServiceCollection, like many other .NET abstractions, is heavily extended through extension methods.
There are reasons for this that are actually part of Microsoft's own framework design guidelines, and I would like to explore them because I find them very cool and useful.
We already see this pattern everywhere in familiar APIs such as IEnumerable<T> and LINQ.
The most important guideline for me is this:
Another very useful property of extension methods is that third parties can grow the apparent API surface without Microsoft ever having to modify the original interface.
A library can simply provide:
The primitive operation exposed by the interface is basically:
GetEnumerator()
Each implementation has to provide its own way of enumerating its elements.
But then things such as:
Where()
Select()
Any()
Count()
First()
and the rest of LINQ can be provided externally as extension methods over IEnumerable<T>.
They don't have to be implemented separately by List<T>, arrays, HashSet<T>, or every other enumerable type.
They operate on the abstraction.
Internally, some LINQ methods can of course detect richer collection types and use optimized paths, but conceptually the functionality is defined on top of the IEnumerable<T> contract.
GetEnumerator() belongs to the interface because every implementation needs to provide the primitive behavior itself.
The richer, derived functionality comes on top.
So, in a sense:
Derived functionality is free.
You implement the minimal contract once, and you immediately get access to a much richer API surface.
I explain:
Basically, if you have functionality that is useful across all implementations of an interface, and that functionality can be expressed purely in terms of the interface's existing members, then it can make a lot of sense to implement it as an extension method.
The important part here is that the logic actually concerns what the interface represents.
If it is unrelated functionality, then it probably belongs in another class/helper/service representing that responsibility.
Take IEnumerable<T>: