Hot Take: C# events > Godot Signals
A while back I opted to connect all my Godot signals using code instead of the editor. I found it easier to follow the logic when I had all signal connections taking place inside my code. I had issues where moving files between directories would break connections. When connecting a signal in Godot, I'd have to change the receiver method from snake case to camel case every time, which I found a bit tedious.
If you have any advantages of Godot signals I'd be happy to hear them.
The main advantages of C# events include:
- Compatibility with types that are not Variant. This includes enums and basically any C# collections.
- Type safety. If I pass the wrong parameters into EmitSignal(), there is no warning.
- C# events can be static.
I was kind of on a roll, so I thought I'd mention some minor points I came up with
- C# event handlers execute in the order they were subscribed. To my knowledge, the order that Signal handlers execute is somewhat opaque and hard to control. Though I wonder if requiring your handlers to be in a given order is a smell.
- You can get return values when you invoke a C# event. If my event uses Func<int> as a delegate (i.e. event handlers have 0 params and return an int, then event?.Invoke() returns the value of the last handler that was executed. I'm dubious as to whether should really be done, but hey, you can do it!
C# events are faster. I made a test where I triggered the same signals 10
billion(EDIT, million, who's a dumbass) times. The results I had were 27ms for C# events, and 4434ms for Godot signals. I'll paste the code should you wish to scrutinise.using System; using Godot;
namespace TerrariaRipoffNNF.testScripts;
public partial class Test : Node {
public event Action CSharpTest; [Signal] public delegate void GodotSignalTestEventHandler();
public override void _Ready() { CSharpTest += TestFunc; GodotSignalTest += TestFunc; TimeMethod(() => { for (int i = 0; i < 10000000; i++) { CSharpTest?.Invoke(); } }); TimeMethod(() => { for (int i = 0; i < 10000000; i++) { EmitSignal(SignalName.GodotSignalTest); } }); } private void TimeMethod(Action action) { var watch = new System.Diagnostics.Stopwatch(); watch.Start(); action(); watch.Stop(); GD.Print(watch.ElapsedMilliseconds); } private void TestFunc() { }
}