Joseph Musser's Avatar

Joseph Musser

@jnm2

Senior software engineer at Microsoft, C# language design member, former 7-year Microsoft MVP

71
Followers
126
Following
37
Posts
20.10.2023
Joined
Posts Following

Latest posts by Joseph Musser @jnm2

The wait is almost over! I'm excited to be joining Microsoft Fabric as a senior software engineer. I'm grateful for this opportunity to tackle new challenges together with an amazing team, stretching the limits of .NET build technology! I'll also continue as a member of the C# language design team.

24.01.2026 20:52 ๐Ÿ‘ 9 ๐Ÿ” 2 ๐Ÿ’ฌ 0 ๐Ÿ“Œ 0

I wanted to keep it simple until some motivating cases came to light. I would love to hear some!

13.01.2026 14:11 ๐Ÿ‘ 0 ๐Ÿ” 0 ๐Ÿ’ฌ 0 ๐Ÿ“Œ 0
A table showing various inputs and outputs of a proposed fullnameof operator:

fullnameof(System.Collections.Generic) 	"System.Collections.Generic"
fullnameof(List<>) 	"System.Collections.Generic.List<>"
fullnameof(global::System.Func<>) 	"System.Func<>"
fullnameof(someExtern::SomeType) 	"SomeType"
fullnameof(ImmutableArray<>.Builder) 	"System.Collections.ImmutableArray<>.Builder"
fullnameof(ImmutableArray<>.Builder.Capacity) 	"System.Collections.ImmutableArray<>.Builder.Capacity"
fullnameof(List<int>) 	โŒ Not permitted
fullnameof(TSomeTypeParameterInScope) 	โŒ Not permitted
fullnameof((int A, int B)) 	โŒ Not permitted
fullnameof(int[]) 	โŒ Not permitted
fullnameof(int*) 	โŒ Not permitted
fullnameof(int?) 	โŒ Not permitted
fullnameof(string?) 	โŒ Not permitted

A table showing various inputs and outputs of a proposed fullnameof operator: fullnameof(System.Collections.Generic) "System.Collections.Generic" fullnameof(List<>) "System.Collections.Generic.List<>" fullnameof(global::System.Func<>) "System.Func<>" fullnameof(someExtern::SomeType) "SomeType" fullnameof(ImmutableArray<>.Builder) "System.Collections.ImmutableArray<>.Builder" fullnameof(ImmutableArray<>.Builder.Capacity) "System.Collections.ImmutableArray<>.Builder.Capacity" fullnameof(List<int>) โŒ Not permitted fullnameof(TSomeTypeParameterInScope) โŒ Not permitted fullnameof((int A, int B)) โŒ Not permitted fullnameof(int[]) โŒ Not permitted fullnameof(int*) โŒ Not permitted fullnameof(int?) โŒ Not permitted fullnameof(string?) โŒ Not permitted

If C# had a fullnameof operator, would it suit your needs if it worked as shown in this image?

Would you let us know? github.com/dotnet/cshar...

13.01.2026 02:54 ๐Ÿ‘ 0 ๐Ÿ” 0 ๐Ÿ’ฌ 1 ๐Ÿ“Œ 0

Being a library author is complicated in so many ways, I totally get the frustration of setting out to do something fun and learning that it explodes into something more nuanced with a longer time horizon than expected!

08.01.2026 15:06 ๐Ÿ‘ 1 ๐Ÿ” 0 ๐Ÿ’ฌ 0 ๐Ÿ“Œ 0

It all depends on whether the library author wants people to start moving sooner, at the cost of having two APIs. I've been in scenarios where I decided one way and scenarios where I decided the other way!

08.01.2026 15:00 ๐Ÿ‘ 1 ๐Ÿ” 0 ๐Ÿ’ฌ 1 ๐Ÿ“Œ 0
08.01.2026 13:45 ๐Ÿ‘ 0 ๐Ÿ” 0 ๐Ÿ’ฌ 1 ๐Ÿ“Œ 0

One way I have dealt with this dilemma as a library author has been to multitarget my library to both 8 and 10, and use `#if NET10_0_OR_GREATER` so that the API only exists when my consumer targets .NET 10 with C# 14, and I can also have the option to give a fallback API or keep the original API.

08.01.2026 13:43 ๐Ÿ‘ 1 ๐Ÿ” 0 ๐Ÿ’ฌ 0 ๐Ÿ“Œ 1

C# 13 also had this potential: the "allows ref struct," "ref struct interfaces," and "overload resolution property" features require C# 13 to be consumed. If used in public API, C# 12 can't even start to understand what's going on. This is a typical state of things.

08.01.2026 13:40 ๐Ÿ‘ 0 ๐Ÿ” 0 ๐Ÿ’ฌ 1 ๐Ÿ“Œ 0

This was the case with records, Default Interface Members, generic math, ref structs, task-like types... and every new language feature that introduced a public API concept that didn't exist in the prior language version, all the way back to C# 2.

08.01.2026 13:34 ๐Ÿ‘ 0 ๐Ÿ” 0 ๐Ÿ’ฌ 1 ๐Ÿ“Œ 0

It's a real dilemma which is par for the course with any brand new language concept that shows up in public API. The older compiler predates the concept, so it won't be able to figure out whats going on when something so new is used.

08.01.2026 13:33 ๐Ÿ‘ 0 ๐Ÿ” 0 ๐Ÿ’ฌ 1 ๐Ÿ“Œ 0
Test plan for "extension indexers" feature ยท Issue #81505 ยท dotnet/roslyn Spec: https://github.com/dotnet/csharplang/blob/main/proposals/extension-indexers.md Championed issue: dotnet/csharplang#9856 Compiler LangVer round-tripping DefaultMemberAttribute ref analysis CRE...

You're right, I misspoke! Extension indexers have not been implemented yet.
github.com/dotnet/rosly...

08.01.2026 13:30 ๐Ÿ‘ 2 ๐Ÿ” 0 ๐Ÿ’ฌ 0 ๐Ÿ“Œ 0

This is pretty much the same dilemma as when Nullable Reference Types came out and using langversion 8 with older target frameworks was unsupported. Or maybe more like the hard limit when Default Interface Members came out and any library that used them required the latest runtime.

08.01.2026 13:26 ๐Ÿ‘ 0 ๐Ÿ” 0 ๐Ÿ’ฌ 0 ๐Ÿ“Œ 0

Nor has anyone proposed, for instance "extension attributes." So "extension everything" just means "a design space for all the rest of the member kinds." That new design space is afforded by the new extension block syntax.

08.01.2026 13:23 ๐Ÿ‘ 0 ๐Ÿ” 0 ๐Ÿ’ฌ 0 ๐Ÿ“Œ 0

It doesn't let you do things like add an interface implementation. That turns out to be incredibly tricky both to implement and to design. For example, the assembly defining such an extension may load later on than the type that it extends. Now do `is IAddedInterface` checks change behavior?

08.01.2026 13:21 ๐Ÿ‘ 0 ๐Ÿ” 0 ๐Ÿ’ฌ 1 ๐Ÿ“Œ 0

It's because it expands the concept of extensions to cover all the member kinds, not just extension instance methods. So far extension static methods, extension properties and static properties, extension indexer, and extension (non-conversion) operators have been implemented, and more are coming.

08.01.2026 13:18 ๐Ÿ‘ 0 ๐Ÿ” 0 ๐Ÿ’ฌ 2 ๐Ÿ“Œ 0

I believe you can foreach over nullable value-typed collections without explicitly writing .Value or unwrapping by other means. The null check still needs to be done before iterating.

07.01.2026 05:57 ๐Ÿ‘ 1 ๐Ÿ” 0 ๐Ÿ’ฌ 0 ๐Ÿ“Œ 0

There's a superb writeup by @stephentoub.bsky.social at devblogs.microsoft.com/dotnet/perfo...!

Updates to it in 9 and 10 too:

devblogs.microsoft.com/dotnet/perfo... and search in page for the first hit of "frozen collections"

devblogs.microsoft.com/dotnet/perfo...

07.01.2026 05:54 ๐Ÿ‘ 2 ๐Ÿ” 0 ๐Ÿ’ฌ 0 ๐Ÿ“Œ 0

Today was my last day at Technology Solutions Associates, after nearly thirteen years total and seven years as a partner. Signing out for the last time is quite a feeling. I'm grateful for the opportunities I had with them, and ready for the next adventure in 2026. Stay tuned!

01.01.2026 01:39 ๐Ÿ‘ 5 ๐Ÿ” 0 ๐Ÿ’ฌ 0 ๐Ÿ“Œ 0

Ever wanted to write `((a, b)) =>` to deconstruct lambda parameters in C#? I've created a proposal and am co-championing the feature now.

Discussion: github.com/dotnet/cshar...
Proposal: github.com/dotnet/cshar...

28.11.2025 23:42 ๐Ÿ‘ 12 ๐Ÿ” 1 ๐Ÿ’ฌ 0 ๐Ÿ“Œ 0
ref var x = ref System.Runtime.CompilerServices.Unsafe.NullRef<object>();

if (x == null)
{
}

ref var x = ref System.Runtime.CompilerServices.Unsafe.NullRef<object>(); if (x == null) { }

I didn't see null refs mentioned yet:

22.11.2025 16:06 ๐Ÿ‘ 2 ๐Ÿ” 0 ๐Ÿ’ฌ 1 ๐Ÿ“Œ 0
App screenshot showing 360ยฐ node viewer

App screenshot showing 360ยฐ node viewer

I've added a 360ยฐ node viewer to my Myst IV Asset Explorer (github.com/jnm2/MystIVA...)!

Rendering a skybox with SkiaSharp in Avalonia, from 3D projection first principles, was satisfying. Looking around at the final result with its layer transparency is surreal.

05.10.2025 14:35 ๐Ÿ‘ 1 ๐Ÿ” 0 ๐Ÿ’ฌ 0 ๐Ÿ“Œ 0
App screenshot showing in-app browsing panes and audio player

App screenshot showing in-app browsing panes and audio player

I made an app to browse and extract #Myst IV game assets, and play audio assets with gapless looping using #dotnet and #AvaloniaUI: github.com/jnm2/MystIVA...

It hits a nostalgic spot to have original game music instead of the official soundtrack's remix; I grew up playing the Myst games.

17.09.2025 01:32 ๐Ÿ‘ 3 ๐Ÿ” 0 ๐Ÿ’ฌ 0 ๐Ÿ“Œ 0

Registered for the 2026 #mvpsummit! Looking forward to flying out and seeing everyone!

17.09.2025 00:26 ๐Ÿ‘ 2 ๐Ÿ” 0 ๐Ÿ’ฌ 0 ๐Ÿ“Œ 0

That's very exciting, thanks for listening! And yes, it's a breath of fresh air to use. Feels well thought through at every turn.

22.05.2025 15:08 ๐Ÿ‘ 0 ๐Ÿ” 0 ๐Ÿ’ฌ 0 ๐Ÿ“Œ 0

@patriksvensson.se I'm finally getting into Spectre.Console and it's spectactular, thank you!

One thing that's giving me cold feet though is that there's no support for named arguments/required options. It seems to me that named arguments are just as important in CLI scripting as in C#.

22.05.2025 13:57 ๐Ÿ‘ 1 ๐Ÿ” 0 ๐Ÿ’ฌ 1 ๐Ÿ“Œ 0

I'm losing the feeling of "what's happening is bad" that used to pop up for me when facing difficulties. Difficulties are the pathway to heart and soul and life and flow.

09.05.2025 01:07 ๐Ÿ‘ 0 ๐Ÿ” 0 ๐Ÿ’ฌ 0 ๐Ÿ“Œ 0
Summary

This feature enables a type name to be omitted from static member access when it is the same as the target type.

This reduces construction and consumption verbosity for factory methods, nested derived types, enum values, constants, singletons, and other static members. In doing so, the way is also paved for discriminated unions to benefit from the same concise construction and consumption syntaxes.

type.GetMethod("Name", .Public | .Instance | .DeclaredOnly); // BindingFlags.Public | ...

control.ForeColor = .Red;          // Color.Red
entity.InvoiceDate = .Today;       // DateTime.Today
ReadJsonDocument(.Parse(stream));  // JsonDocument.Parse

// Production (static members on Option<int>)
Option<int> option = condition ? .None : .Some(42);

// Production (nested derived types)
CustomResult result = condition ? new .Success(42) : new .Error("message");

// Consumption (nested derived types)
return result switch
{
    .Success(var val) => val,
    .Error => defaultVal,
};

Summary This feature enables a type name to be omitted from static member access when it is the same as the target type. This reduces construction and consumption verbosity for factory methods, nested derived types, enum values, constants, singletons, and other static members. In doing so, the way is also paved for discriminated unions to benefit from the same concise construction and consumption syntaxes. type.GetMethod("Name", .Public | .Instance | .DeclaredOnly); // BindingFlags.Public | ... control.ForeColor = .Red; // Color.Red entity.InvoiceDate = .Today; // DateTime.Today ReadJsonDocument(.Parse(stream)); // JsonDocument.Parse // Production (static members on Option<int>) Option<int> option = condition ? .None : .Some(42); // Production (nested derived types) CustomResult result = condition ? new .Success(42) : new .Error("message"); // Consumption (nested derived types) return result switch { .Success(var val) => val, .Error => defaultVal, };

Discussion for a proposal I'm working on: Target-typed static member lookup
github.com/dotnet/cshar...

20.01.2025 19:10 ๐Ÿ‘ 5 ๐Ÿ” 0 ๐Ÿ’ฌ 0 ๐Ÿ“Œ 0

No, but this looks amazing! What is it doing differently from 'nuget why' that causes the binlog viewer to show < 47k lines of output for this? Is it excluding deeper-nested packages if the package is already present at a level nearer to the project?

14.12.2024 00:51 ๐Ÿ‘ 0 ๐Ÿ” 0 ๐Ÿ’ฌ 1 ๐Ÿ“Œ 0

There should be a word for the feeling of wrapping up from being ten layers deep into something, specifically finishing a bunch of tasks in a row as you unwind your stack, which you had been holding in memory. There's something satisfying about hitting all of them on the way out.

14.12.2024 00:22 ๐Ÿ‘ 2 ๐Ÿ” 0 ๐Ÿ’ฌ 0 ๐Ÿ“Œ 0
Preview
'dotnet nuget why' option to show unique parent packages: --depth 1 ยท Issue #45464 ยท dotnet/sdk The problem happens when you reference a large set of deeply nested third-party packages that all have common dependencies. When you run dotnet nuget why Microsoft.CodeAnalysis.NetAnalyzers, and ge...

Have you ever run 'dotnet nuget why' and gotten a ridiculous or impossible amount of output to wade through? Would you be interested in a --depth parameter, or something similar?

github.com/dotnet/sdk/i...

13.12.2024 23:48 ๐Ÿ‘ 1 ๐Ÿ” 0 ๐Ÿ’ฌ 1 ๐Ÿ“Œ 0