The compiler simply needs certain features to exist to bind to them. In the case of (string Name, int Age)
it is looking for System.ValueTuple<T1,T2>
- and as long as it finds that, it is happy. It doesn't usually matter from where - if it comes from the current runtime system library: fine; if it comes from a nuget package: also fine!
This has a lot of pedigree; this same approach applies to a lot of things:
- "LINQ" - just needs the right methods to resolve, whether by extension methods or not (this has allowed "LinqBridge", for example, to retro-fit LINQ onto older runtimes)
- async/await - just needs the
GetAwaiter()
etc methods to exist, whether by extension methods or not
- async iterator blocks and async foreach - just needs the
IAsyncEnumerable<T>
etc APIs to exist (and the correct builder APIs for async iterators)
- async dispose - just needs the
IAsyncDisposable
interface to exist
- etc
so .NET and C# have enjoyed a long history of allowing nuget packages (or other library updates) to "light up" C# features that are designed for "current" frameworks.
Without those methods / types / etc being available, the compiler still knows about the feature - it just can't use it unless it knows how to attach. If the type / method resolves: you're golden.