Proposal: Allow null conditional (?.) and null coalescing ()?? operators to be overloaded #2020
Replies: 58 comments 77 replies
-
For the confused reactions: Unity has a type called These objects can be null-ish when they exist in Mono but don't exist in the engine. Unity overrides the equality operator to reflect this behavior (Implemented by In my opinion, this issue is a weird quirk of how Unity treats objects in general. I don't think it's really something that makes sense to change in the language. IMO, if this were done, it'd make more sense to allow overriding @Alexees What are you doing where you keep running into these zombie objects? I always knew they existed in Unity, but I've never actually run into them in practice.
This sounds like a poor design choice in the Unity tools for Visual Studio or in the If you happen to be running into this with public override string ToString()
=> this ? base.ToString() : "<Zombie>"; |
Beta Was this translation helpful? Give feedback.
-
Consider this piece of code
breaking into the line that uses the null coalescing operator, in the case |
Beta Was this translation helpful? Give feedback.
-
That seems like a behavior that could awkwardly rear its head all over the place resulting in NREs at runtime. I'd suggest that maybe this is something that Unity should resolve through its runtime rather than having the language attempt to gloss over it? |
Beta Was this translation helpful? Give feedback.
-
Oh weird, sorry for assuming you were doing something unusual. I will note that this doesn't happen with references to other components ( I suspect this is also why I've never run into it. I generally use references to specific components on other game objects rather than references to the game objects themselves. Also, slight nit-pick here: It throws It's also worth noting that zombie instances do look slightly different in the debugger: (References with the value of I agree with @HaloFour. This is something that Unity should change rather than C#. The deserializer shouldn't assign these zombie objects to unassigned fields. However, I don't see Unity changing this anytime soon. I wouldn't be surprised if there's an actual reason for it too. It's not pretty, but you can workaround this quirk using self-reflection in your |
Beta Was this translation helpful? Give feedback.
-
@PathogenDavid thanks for your insights and thoughts |
Beta Was this translation helpful? Give feedback.
-
Roslyn only shows 'null' for constants that are set to null. Can you be more specific about what is showing the value as 'null? |
Beta Was this translation helpful? Give feedback.
-
@CyrusNajmabadi What he was seeing is Unity returns the string |
Beta Was this translation helpful? Give feedback.
-
I asked @xoofx on Twitter and he was able to provide some insight into why Unity works this way. Short version: The fake null values are an editor-only feature to display more helpful error messages when you use a null-ish reference: The important takeaway here is that this only happens in editor builds. In standalone player builds, the references are literally As such, the workaround is only necessary for editor builds. I've updated my workaround to reflect this. Also do note that nullish Unity objects can still happen in the case of destroyed objects. So this workaround is only applicable when you're dealing with a freshly deserialized object. Proper language support like #147 would still be necessary if you want null-coalescing to work in these situations. (Although I'd argue that if you are keeping references to dead Unity objects then you have a bug anyway.) |
Beta Was this translation helpful? Give feedback.
-
Did Unity ever make a decision or are we still rolling on inertia? Considering that the .NET Framework debugger (and I think .NET Core too now?) can tell you which variable was null when you get a There's a similar issue with |
Beta Was this translation helpful? Give feedback.
-
(Micro disclaimer up front: I say "Visual Studio" a lot here when "A modern .NET debugger like Visual Studio" might be more accurate.)
There was no update in the comments, but they are apparently riding on interia. That article was published 4 years ago before Unity 5. My test runs performed in this thread were performed with Unity 2018.3.0b6 (that's a recent beta release, although not the latest.)
It's maybe worth mentioning for the sake of perspective that Unity doesn't let Visual Studio (or other debuggers) debug exceptions uncaught. Exceptions are always swallowed by the engine and appear in the console: (Yes this is infuriating. It's my #1 issue with Unity as a game engine.) Unity only shows the exception message. I believe the faulting reference getting shown by Visual Studio is a Visual Studio feature. One exception is you can tell Visual Studio to always break when a For reference for any Unity users wandering in here (or people like me who forgot Visual Studio does this), this is the feature @yaakov-h is referring to:
Strong agreement here. I was pretty disappointed to learn that they considered and didn't take the breaking change in Unity 5. Quirks like this are why I'm not a huge fan of Unity.
Unity is currently stuck on C# 6.0 and .NET Framework (equivalent) 4.6, so they don't even have pattern matching at all yet. (The latest Unity betas have support for Roslyn, C# 7.3, .NET Framework 4.7.1, and .NET Standard 2.0. That all is supposed to drop by the end of the year.) I'm not sure if this quirk will cause any more problems than it already does. It mainly comes up when fields with specific types are serialized as null and later deserialized. |
Beta Was this translation helpful? Give feedback.
-
Yep, it's information exposed by the debugger APIs starting with .NET Framework 4.6.2. https://docs.microsoft.com/en-us/dotnet/framework/whats-new/#Debug462 |
Beta Was this translation helpful? Give feedback.
-
Unity is a very bad game engine. This is a problem that Unity needs to fix, and not one that the entirity of C# needs to suffer for. The last thing we need is unknowingly having |
Beta Was this translation helpful? Give feedback.
-
While we are at it why don't we also allow custom operator overloading across the board.... heck why not even totally custom operator$? .][><_/=÷×+)(*&%$#@! ®... Tl;dr... Operators are useless in c# when I can ref up a method to perform the same logic with less overhead. The benefit IMHO comes when the jit can elide certain check and or create better runtimr code e.g. as it does with == / ceq on occassion, that being typed... If operator overloads could be used with ref operators I could live with that enhancement as well... |
Beta Was this translation helpful? Give feedback.
-
So am I correct in the assessment that the developers of Unity are too stubborn to change the way they're doing things, and the developers of .NET are too stubborn to allow any additional functionality, so the end-result is that end-users of C# in Unity are stuck with a sh*tty experience— while all the developers who can actually improve things would rather point fingers and blame and shade. This would be the perfect time to throw shade at both Unity and .NET for the rampant bugs and antiquated APIs both have suffered from over the years, but… that's just mean-spirited. The reason why I'm willing to be a d*ckhead here is to make it clear that if using your platform is a sh*tty experience, people will leave— regardless of whether it's your fault or not. I know I have mostly abandoned Unity & C# as much as I can for alternatives I prefer— Unreal, Godot, C++, Rust, Swift. Grow up and come up with solutions. There's plenty can't in the above comments— the same attitude that's all common among the core .NET/C# developers' blog posts over the years. If you can't then I and others can abandon you. Again, I'm sorry about I have to be a d*ckhead, but sh*t rolls downhill, and I'm tired of being sh*t on. |
Beta Was this translation helpful? Give feedback.
-
This would be something you could recommend over at dotnet/corefx. Thanks. |
Beta Was this translation helpful? Give feedback.
-
You need to step back. You're not dealing with pragmatic product decisions in a healthy manner. No language out there satisfies its entire audience all of the time. And pretty much all of us have worked in environments where the language we had to use made decisions that we didn't like. It's part and parcel of being a software developer. Personally, my experience has had me use actively for the past 20 years: C++, Java, ObjectiveC, Swift, Python, Go, C#, VB, TS, JS (and many others). Every single one of these languages has made decisions that have actively made my professional life harder and have caused extra work to be necessary when compared to hte others. That's just life. I strongly ask that you step back, take a breath and not allow yourself to get so mentally hung up on these things. It's never going to be beneficial for you or your career. It will also interfere greatly with your attempts to see change happen. After all, if it causes you to interpret decisions as "hostility" and the like, then you'll only catch yourself up in a vicious cycle of frustration and anger. |
Beta Was this translation helpful? Give feedback.
-
Wow, that was a long debate. Sorry for everyone if my proposal got you headaches. |
Beta Was this translation helpful? Give feedback.
-
At this point the feature request has been made. It will be up to the LDM to decide if they want to take it. At hte very least though, i would recommend putting forth a full proposal. i.e. one that outlines precisely what change one would like to see in the language and covers important details like: "is htis a breaking change?" It should also go in depth into potential alternatives and why they are not acceptable. The proposal can optionally come wiht an implementation PR. That can help but is def not required. If a good proposal can be made that meets the goals of the C# team and substantively provides a reasonable path to get there, it has a change. If the proposal actively goes against C# goals (like 'no breakin changes') or doesn't address serious alternatives, then i don't think it can be taken seriously. For example, it would need to seriously address why solutions with extension-methods/analyzers aren't acceptable. Those would be avialable now at costs 3-4 orders of magnitude less than changing hte language. the value of this work would have to demonstrate that it was getting that sort of payoff to warrant the investment into it. TLDR: feature requests that are effectively no more than "i don't like this, plz make this better" won't go anywhere. The feature request has to be in depth and has to act similar to a thesis where the problem is effectively stated and the solution is well defended across the board. |
Beta Was this translation helpful? Give feedback.
-
I'm just a Dev, not a language designer so you don't consider myself capable of providing a proposal with in depth details about it being a being change or not. |
Beta Was this translation helpful? Give feedback.
-
Overloading ?. would be very clean syntax for types which encapsulate a reference-by-id to an object that is not necessarily resolved yet, but should be on demand. I want to treat the wrapper type as if it were an actual object of the inner type, which i could do by attempting the resolve and returning null or the other type when ?. operator occurs. |
Beta Was this translation helpful? Give feedback.
-
As a developer with over 10 years of experience using Unity, I believe that Unity made some mistakes in the early design of its API, including overloading the On the other hand, considering the complexity of equality comparisons in the C# language and runtime, another thought I have is that perhaps we shouldn't make it more complicated... Let's stop the confusion here and moving forward, let's write code using the recommended approach. I've heard that Unity is addressing this issue, so we'll wait and see. Additionally, I've noticed that some similar proposals have been locked, is it because you already have conclusions about such proposals? |
Beta Was this translation helpful? Give feedback.
-
Note that my use case shows something not unity specific, and also very clearly not the usual usage of ?. (so, not ambiguous) because it would be used on a struct representing some other class type object, and the current alternative is having a method call like .Get()?. everywhere. In fact I want a method call, that I get to write, but the syntax could be a lot nicer. |
Beta Was this translation helpful? Give feedback.
-
You can use this asset from the asset store to use the null conditional and null propagation operators safely on GameObjects: https://assetstore.unity.com/packages/tools/utilities/smooth-operator-271378 |
Beta Was this translation helpful? Give feedback.
-
Looks great but pretty pricey for implementing a way to overload the null conditional and null coalesce operators. As a developer if you can override the
And if you don't explicitly override these, then they'd continue working as expected: all existing code continues to function exactly as it historically has. In C# we haven't been able to override these operators previously, so moving forward anyone wanting to use this would be explicitly opting in, such as Unity only with their Providing this extensibility support should genuinely have no out-of-the-box negatives, and would only break existing code if developers override these operators with arbitrary "bad" definitions - but likewise, overriding the Yet if one developer is disorganized to the point of writing a "bad" operator overload, perhaps without considering inheritance, and ends up fighting against oneself, why should that prevent an organized developer from being able to write calculated operator overloads in a given use case? The developers using Unity quite literally need only to override If you don't want to override these operators, then don't - and still, having added this support, presumably everybody wins. Right? |
Beta Was this translation helpful? Give feedback.
-
I think bringing up unity, despite the original poster having done so, it's just going to be a distraction for getting something like this actually done. I think the next step is someone to actually write a full proposal. Actually question for someone more important than me around here: what is the next step here? |
Beta Was this translation helpful? Give feedback.
-
Unity is just a very tangible use case. Development is always a balance between theory and pragmatism, and in this case, there is a very real pragmatic need, if a very large portion of game developers are to enjoy this quality of life in C# itself.
Does the (current) lack of a supporting Language Team member negate the following?
|
Beta Was this translation helpful? Give feedback.
-
This seems to just be a debugger display for the item. It's not actually
That's a choice unity has made in its debugger display. The debugger could have a
No, it doesn't need to be that. I'm saying: |
Beta Was this translation helpful? Give feedback.
-
If this is truly meant to be the only purpose of Consider the following simple snippet: string s = null;
Console.WriteLine(s?.ToString() ?? "is null"); // prints "is null"
Console.WriteLine(s.ToString()); // throws a NullReferenceException This works exactly how you'd expect - the Now consider the exact same thing with a nullable value type set to null: int? n = null;
Console.WriteLine(n?.ToString() ?? "is null"); // prints "is null"
Console.WriteLine(n.ToString()); // does not throw, prints an empty line Clearly the So nullable value types are, effectively, doing what you don't want to allow developers to do: overloading the concept of being null to work more smoothly with other language features. Granted, it's doing some special compile-time transformations that wouldn't necessarily work with custom operators (on sharplab.io you can see that it's transforming the |
Beta Was this translation helpful? Give feedback.
-
I won't dive into the discussion about Unity's "fake nulls", but I suppose there's a workaround for that, which not only "solves" the problem, but also doesn't require a newer language version: Just remember that C# allows the With this approach, the Unity team could override the UnityEngine.Object obj = GetObjectA() || GetObjectB() //GetObjectB is called only if GetObjectA returns a nullish.
string s = (obj || null)?.ToString() //ToString is not called if 'obj' is nullish. |
Beta Was this translation helpful? Give feedback.
-
I'm asking this in favour of the Game Engine Unity. Since the core engine is written in C++ and scripts are written in C#, == and != are overriden. This means even though serialized fields (public or SerializeFieldAttribute) which are reference types, get an instance of the corresponding C# class created during construction. In these cases those operators still return null because there has not been any C++ equivalent constructed.
Now since ?. and ?? have made their introduction, they cause a lot of confusion as you have to know when they can be applied and when they cannot. Here, they cannot. Visual Studio shows the variabled as being null, but they're still chosen despite this fact and cause NullReferenceExceptions afterwards.
If they were overridable they could just implement the same logic and we all could use this syntax without hesitation.
Beta Was this translation helpful? Give feedback.
All reactions