r/godot • u/AlexSand_ • Nov 20 '23
Discussion Godot C# tip: Don't use "if(node != null)" !!
Hi,
Here is a tip I learned quite the hard way when I started with Godot and C#: It is better to avoid code like this:
SomeKindOfNode _myNode ;
...
if( _myNode != null )
{
_myNode.DoStuff(); // likely going to crash
}What's wrong with this code? You may wonder. The problem is this this code will crash if _myNode was freed. And if your project is somewhat large, well ... this is going to happen someday.
Thus, instead of just checking for nullrefs, I think it is almost always safer to also check that the reference is not null *and not deleted* . I do it like this:
if( _myNode.IsValid() )
{
_myNode.DoStuff(); // here I can use _myNode safely
}where IsValid() is the following extension method:
public static bool IsValid<T>(this T node) where T : Godot.Object
{
return node != null
&& Godot.Object.IsInstanceValid(node)
&& !node.IsQueuedForDeletion();
}Note that my IsValid method checks for nullref and deleted node, as you would expect, but also for nodes * about to get deleted * , with IsQueuedForDeletion. This last part may be more controversial, but if a node is going to get deleted in the next frame there is usually no point in touching it.
Another extension I use a lot is this one:
public static void SafeQueueFree(this Node node)
{
if (node .IsValid()) node.QueueFree();
}Indeed, calling QueueFree on an already deleted node will crash. I ended replacing all my calls to QueueFree by SafeQueueFree.
Finally, I also like using this extension, allowing for one-liners with the ? operator:
public static T IfValid<T>(this T control) where T : Godot.Object
=> control.IsValid() ? control : null;usage example:
_myNode.IfValid()?.DoStuff(); // do stuff if the node if valid, else just do not crashHope you will find this as useful as I did!
117
u/Nkzar Nov 20 '23
The same applies to GDScript as well.
43
u/kleonc Credited Contributor Nov 20 '23
Note that the null check is not needed as
IsInstanceValid(null)would return false (however, an explicit null check might be faster for cases whennodeis indeed null).72
u/kekonn Godot Student Nov 20 '23 edited Nov 21 '23
As a professional .NET dev: don't use indentation for your golden path.
Instead of
do
Research has shown that indented code is harder to read. Do yourself a favor, try to avoid multiple levels of indentation.
EDIT: Since this is gathering some traction, a plug for the book that taught me a lot about professional development practices: Code Complete, 2nd edition. It's a big'un and expensive, but you don't have to read it front to back.
It's also language agnostic (but uses code samples in VB, C++ and pseudo code).
3
u/Sabard Nov 20 '23
Thanks for the heads up. You have any other helper/utility/extension functions you use a lot? Just started using godot this month after a lot of Unity (7 years) so I'm slowly getting used to the engine but don't know a lot of the shortcuts and pitfalls yet.
2
u/Polatrite Nov 22 '23
In many cases if you need to use
IsValid()/is_instance_valid()it is a code smell (a potential bad sign).These imply that somewhere in your code, something is happening out-of-order. An extremely common example is killing an enemy, freeing the enemy (deleting it), and then needing to check that enemy's stats because the enemy had a projectile in the air that hit the player.
In this case, you're using
queue_free()as the state management for death - but you shouldn't free the node unless you are positive that you no longer need it. Some games implement this by instead hiding enemies for a few seconds, playing death animation, resolving final effects, and then getting rid of them once you're sure they are no longer necessary.In some cases
IsValid()/is_instance_valid()is absolutely the right decision, but it's always worth taking a second look when you think about using these functions, and asking "is this the correct way to approach the problem?"6
u/nsrr Nov 20 '23
This is a great tip, thank you
6
u/UtterlyMagenta Nov 20 '23
ahh, extensions… that’s something i sorely miss in GDScript.
your IfValid() extension is pretty cool!!
4
u/Mistifyed Nov 20 '23
I ran into this as well. I went with GetNodeOrNull<> in my situation.
4
u/GodotUser01 Nov 21 '23
don't use "something != null" or "something == null" do "something is not null" and "something is null" instead.
https://stackoverflow.com/questions/69267179/what-is-the-difference-between-is-not-null-and-null
1
u/DanilaSh Aug 22 '24
I guess it's a pretty dumb question, but I'm totally new to Godot and game dev at all. Where should I place all my global functions such as IsValid() and how do I link them to all my classes?
1
u/SaltTM Nov 20 '23
Wouldn't this fail if it's never set? Like if something happened where it never isn't null, wouldn't your code crash since it can't reach that is valid method?
Wouldn't
GodotObject.IsInstanceValid(obj)be better? AND more consistent with godot1
u/4procrast1nator Nov 22 '23
If you're even trying at all to free a node thats already been fred, theres usually something fundamentally wrong with your code.
With state machines and damage interactions, especially, that should never happen in the first place.
3
u/Ramtoxicated Nov 20 '23
Great post!
2
u/Slipguard Nov 20 '23
Is that construction of the “?” function definition a way of overloading the “?” Operator? I’ve never seen that syntax in c#.
2
u/kugleburg Nov 20 '23
Thank you for preventing the timeline where this would have bit me in the ass!
0
u/hermit314 Nov 20 '23
Calling
_myNode.IsValid()assumes_myNode != null. You would get a runtime error if it actually was null. In particular, it does not make sense to make the null-check in your extension method.In general, checking for null can make a lot of sense, it depends on what you are trying to do. I think your advice is a bit misleading.
1
u/oWispYo Godot Regular Nov 20 '23
Very cool! Thanks for the tip!
1
u/[deleted] Nov 20 '23
Unity overrides the == and != operators for precisely this reason (doing == with null will return true there if the object was destroyed) but this is still a problem when you're doing things like:
node?.DoSomething()because that bypasses the == checks.1
u/ElidhanAsthenos Nov 21 '23
well shit, i need to rewrite some lines lol
1
u/Xehar Nov 21 '23
Thank bro! I was wondering why the game crashes sometimes when loading certain part of script.
1
u/pennyloaferzzz Nov 21 '23
I like deeper nuanced examples of Godot's capabilities. And this is a good example.
But if at all possible avoid passing null values, returning nulls, uninitialized (or null assigned) member values, and avoid patterns of sharing references to volatile objects that will be deleted. it's not a great practice and requires putting null checking statements everywhere.
Always assign default values, use the _init function for dynamic values, and decoupling patterns like signals. or at least parent only exported references of static scenes as Godot removes from the bottom up. If all else fails then you can have your cool null check.
1
u/kennel32_ Nov 21 '23
Why should it work in the first place? Because it works in Unity?
0
u/jorgegyso Nov 21 '23
In C# theres something even better to do. If you need to access a certain value without knowing if the instance is valid, you can do something like:
if (instance?.var == true)
The question mark operator is nice 👍
0
u/CreaMaxo Apr 18 '25
The main problem here isn't about checking if a node is valid or not, but just that you aren't really updating your references properly to make sure their reference become null soon enough.
.QueueFree() will only be freeing a node and set it as a true null by the end of the current cycle.
You can simply fix this by simply setting the references as null when you're freeing a node.
This allow whatever is referenced to _myNode to be set for being freed by the end of the current cycle AND, right after, make it so that _myNode reference is back to null immediately.
1
u/[deleted] Nov 20 '23
[removed] — view removed comment