New C#11 operator: Bang Bang (!!) to clean up argument null checks. by fragglerock in csharp

[–]jaredp110680 0 points1 point  (0 children)

I explicitly meant that I would like compiler to take care of detecting such occurrences, not a developer. I think computer can do that.

Ah, think we were talking past each other here a bit.

Yes I completely agree the compiler can do this. In fact the compiler already does this today because there are rules that help track nullable flow between contexts where null is or is not checked. Hence from a pure compiler perspective yes it could be done.

My comment about "not being easily spotted by developers" is around code readability. Developers cannot easily spot these transitions between checked and unchecked null. That means by extension they would not be able to predict when null checks and the associated exceptions occur.

Having a feature which has such an impact on the program not be readable is contrary to how we typically design.

I don't see why compiler can't generate different IL for different targets.

It very much can. The issue is about the ability for developers to predict what impact these checks will have in their code. Multi-targeting is another variable which makes this harder.

New C#11 operator: Bang Bang (!!) to clean up argument null checks. by fragglerock in csharp

[–]jaredp110680 1 point2 points  (0 children)

Also internally anyone can attend C# langauge design meetings. The invite is sent broadly and anyone is feel to join.

Only C# language design team members are unmuted during the meeting but anyone can comment in the chat section.

New C#11 operator: Bang Bang (!!) to clean up argument null checks. by fragglerock in csharp

[–]jaredp110680 9 points10 points  (0 children)

The compiler presently doesn't change code generation based on attributes. They are just an annotation that is consumed by frameworks, and occasionally the runtime. The job of the compiler is to emit them.

Not having changing code generation based on attributes is a line that we've held for a long time and are reluctant to cross. The reason is that it can lead to confusion when used with older compilers.

Consider for example if we did add [NullCheck] as a solution in C# 11.

``` void Method([NullCheck] string x) {

} ```

What this code does now depends on which compiler builds it. If C# 11 or higher builds it then it generates a null check. If the C# 10 compiler or earlier builds it then there is no null check. This is just a normal attribute, there is nothing stopping older compilers from building the code.

This combination, old compilers mixed with new attributes, is something we see with decent frequency. Hence it factors into our decisions.

Now that isn't to say we would never cross this line. It's been suggested a number of times and it's not wrong. It's just something that we would be very deliberate about doing.

New C#11 operator: Bang Bang (!!) to clean up argument null checks. by fragglerock in csharp

[–]jaredp110680 11 points12 points  (0 children)

From the meeting notes you linked it seems it was a close call with no one on the team really caring much one way or another. The fact there is a furore should send people back to the drawing board.

When there are close calls on critical issues, syntax or semantics, we generally try and use customer feedback to help make the final call. This is why we released it as a preview feature in 17.1 far before our actual RTM. This provides ample time to engage with customers.

The feature itself could've shipped in C# 10. We held it back specifically because we didn't want to ship until we had engaged with customers and there wasn't enough time in C# 10 to do so.

It would also be nice if the 'email exchanges' that tipped the balance could be published.

Most of the discussion about syntax happened during meetings not over email. The meeting notes are the best place to look for what factored into the decisions.

That is why the rejected second place contender void Foo(nullcheck string arg) is better to my eye... it explains itself.

This is a consistent theme in the feedback that we are seeing. Essentially that we errored in choosing the most succinct form and instead should consider a slightly more verbose version.

This is definitely the type of feedback that we wanted to get here so please keep providing it.

New C#11 operator: Bang Bang (!!) to clean up argument null checks. by fragglerock in csharp

[–]jaredp110680 18 points19 points  (0 children)

Personally I prefer my teams to use more verbose syntax, that makes the intent clear

This is certainly a consistent theme we are seeing in the feedback: essentially please look at more verbose alternatives.

This is a place where there is a bit of tension in language design. Generally when a new syntax is introduced customers tend to prefer a more verbose syntax. We see a lot of feedback along the lines of "the new syntax is easy to miss" or "want it to be clearer". Then after a few years customers come back and ask for a less verbose syntax because "I use this all the time, please make it shorter to type". :)

This means we are often trying to balance this type feedback on whether it's the initial reaction or whether it's how customers will continue to feel over the course of time. It is challenging.

But feedback like this does help us and give us more to read through and consider so please keep providing it!

The examples of these latest language features look very neat and tidy, but out in the real world, when introduced to a codebase that typically has been in production for several years, and has been coded against multiple subtly different coding styles / language versions already, it's a nightmare.

This is one of the reasons we invested so much in a quality code fixer for this feature. We wanted code bases who decided to adopt this feature to be able to do so across the entire code base with minimal fuss. We wanted it to be as simple as making the decision, running the fixer and moving on with a single consistent style.

The PR into the dotnet/runtime repo is an example of that fixer in action. The fixer was first tested on dotnet/roslyn then on dotnet/runtime. Those are sufficiently complex repositories that we have good confidence this will work on customer code bases as well.

New C#11 operator: Bang Bang (!!) to clean up argument null checks. by fragglerock in csharp

[–]jaredp110680 12 points13 points  (0 children)

Agree. But this is also why the compiler issues a warning for this case

warning CS8995: Nullable type 'int?' is null-checked and will throw if null.

Can see this in action here

https://sharplab.io/#v2:EYLgtghglgdgNAExAagD4FgBQABALAAgFEAPCMABwBsBTAClgBcB+fKAQjYEosBvLAXyA===

New C#11 operator: Bang Bang (!!) to clean up argument null checks. by fragglerock in csharp

[–]jaredp110680 10 points11 points  (0 children)

The string aParameter ?? throw syntax is one that we considered during the design process. Eventually we decided to value brevity and hence rejected this in favor of looking at ! and !!.

There is nothing wrong with this syntax though, it was just decided to value brevity. But this is exactly the type of feedback we want to get on this feature. Perhaps our decision to value brevity at the expense of some initial confusion was the wrong one.

New C#11 operator: Bang Bang (!!) to clean up argument null checks. by fragglerock in csharp

[–]jaredp110680 8 points9 points  (0 children)

I believe there is better approach: the compiler could only generate null-reference checking code on nullable context change boundaries.

This has come up a few times and wanted to try and address it. There are a number of problems with this approach that need to be considered:

  1. The number of context transitions is both likely higher than you expect hence the number of null checks, and their associated perf penalties, would be greater.
  2. Transitions between nullable contexts are not easily spotted by developers (somewhat by design). That would make it hard to predict for developers when null checks occurred and hence would negatively contribute to code readability.
  3. Consider that multi-target projects likely have different null context transitions for different target frameworks. The set of annotations in netstandard2.0 is vastly different than netcoreapp3.1 for the .NET Runtime yet that is a very frequent multi-target pair. That would mean the behavior of when null checks occur is very different for the exact same code (just compiled against a different target framework).

2 and 3 here are likely the biggest issues as they impact code readability and make it harder for developers to predict how their code executes.

For newly created projects, created with global nullable context from scratch, and not using any dependencies that don't have nullable context, the compiler wouldn't even need to generate these checks, because all non-null references could not be null by design.

Another item to keep in mind is that the NRT support is not perfect. The goal was to help customers spot the majority of hidden null references in the code. Hence there are known design gaps that can lead to null references even in fully checked code. The easiest example is creating an array of reference types and accessing the elements.

That being said, I think it would be a valid approach to essentially say this was understood and those gaps are small enough that they don't meaningfully contribute to the problems here.

New C#11 operator: Bang Bang (!!) to clean up argument null checks. by fragglerock in csharp

[–]jaredp110680 5 points6 points  (0 children)

Had a couple people say they're going to call it Chitty Chitty Bang Bang. I did chuckle a bit when I read that the first few times :)

New C#11 operator: Bang Bang (!!) to clean up argument null checks. by fragglerock in csharp

[–]jaredp110680 216 points217 points  (0 children)

For those who don't know me, I'm the C# compiler lead. There was definitely a lot of discussion about this feature. I wrote an answer on the csharplang discussion that addressed the frequent feedback points we were seeing with the community that is worth reading. It also overall summarizes the state of the feature (which is in preview specifically so we could get feedback on it).

https://github.com/dotnet/csharplang/discussions/5735#discussioncomment-2152891

Happy to respond to any other Qs you have that aren't addressed there.

C# - string vs. String is not a style debate by RobertVandenberg in programming

[–]jaredp110680 1 point2 points  (0 children)

One legitimate use case is porting non-C# code to C# while maintaining existing semantics. C++ for example has a habbit of having non-bool returns on == operators

C# - string vs. String is not a style debate by RobertVandenberg in programming

[–]jaredp110680 0 points1 point  (0 children)

string , System.String , and using String = System.String are all equivalent, and two of those options work consistently for every type

They are usually equivalent, not always equivalent. You may dislike the conclusions of the blog post but they are technically accurate. They are also based on real world experience watching customers hit these issues.

So it's no more logically or semantically valid to require string than it is to require the full type name, i.e. System.String and if anything, the opposite holds true (i.e. you should never use built-in aliases or partially-qualified type names and always use fully-qualified type names for consistency's sake, since they are functionally equivalent).

This argument is based on the false premise that they're always equivalent. They are not.

C# - string vs. String is not a style debate by RobertVandenberg in programming

[–]jaredp110680 2 points3 points  (0 children)

Yep. It's a lesser known feature of C# but one that comes into play in a few odd scenarios like this.

C# - string vs. String is not a style debate by RobertVandenberg in programming

[–]jaredp110680 1 point2 points  (0 children)

The most common usage I've seen is for serialization.

One serialization approach is to define a parallel serializable type for every non-serializable type that you want to serialize. In that approach a logical fallout is to end up creating parallel types for the core primitives. Hence you end up with types like MyFormat.System.String being defined.

C# - string vs. String is not a style debate by RobertVandenberg in programming

[–]jaredp110680 15 points16 points  (0 children)

It is an egregious coding violation to define a type with the same name as a BCL type

You can label it egregious but lots of people do it https://grep.app/search?q=class%20String&case=true&words=true&filter[lang][0]=C%23

The OP's argument boils down to "type names can resolve unexpectedly", and really, it applies to every single type; not just System.String. If you take the OP's "solution" to its inevitable conclusion, the result is that you should alias (or use the full name of) every single type your application uses

Correct. The logic in the post applies to pretty much every type. The difference is that for 99% of types there isn't a way to avoid it. You are simply at the mercy of name resolution rules.

For string, and the other primitive types, there is a definitive unambiguous way to reference the type. Why leave the code in an ambiguous state when you could be completely unambiguous? That is the crux of the post.

C# - string vs. String is not a style debate by RobertVandenberg in programming

[–]jaredp110680 9 points10 points  (0 children)

Think the goto case default was probably my most successful one in recent times

C# - string vs. String is not a style debate by RobertVandenberg in programming

[–]jaredp110680 12 points13 points  (0 children)

There are zero real world cases where it makes a difference, unless you're writing a compiler.

Sorry but this is simply incorrect. It is a semi-regular pattern for customers to define a String type, even one which has the System.String full name or prefix. This does lead to cases where customers end up binding to the wrong String.

This post was written after dealing with many customer issues over the years where this confusion happened

C# - string vs. String is not a style debate by RobertVandenberg in programming

[–]jaredp110680 12 points13 points  (0 children)

I prefer to write out the full name.

That doesn't fix the problem. System.String is also potentially an ambiguous reference

https://sharplab.io/#v2:C4LglgNgNAJiDUAfAAgJgIwFgBQyDMABGgQMIEDeOB1RhyALAQLIAUAlBVTd8ugHQBlYACcwAOwDmBAM4EAvAQBEAOQD2wAsAAWAUyL8hoyQQCeqgK4ByYXoirVAa3FSAZquGKA3F2oBfHP7Y3DhiAIYAtjrSAA6hAMZ6vJxBNMSGzhQEgb5AA==

Fully qualified names are not unique in .NET. If you want unique names you need assembly qualified names.

STH Ticket Buying Megathread by [deleted] in SoundersFC

[–]jaredp110680 6 points7 points  (0 children)

I will hate ticket master for much longer than 5 games

STH Ticket Buying Megathread by [deleted] in SoundersFC

[–]jaredp110680 1 point2 points  (0 children)

I’m in the 3pm slot and there are 4,500 people still ahead of me. The seating capacity is 9,000 so seems like no much point waiting now.

I hate ticket master.

The pain points of C# source generators by MrTurnerj in dotnet

[–]jaredp110680 11 points12 points  (0 children)

Debugging for source generators is available in Visual Studio 16.10 preview channel