godotengine / godot

Godot Engine – Multi-platform 2D and 3D game engine
https://godotengine.org
MIT License
88.88k stars 20.15k forks source link

When C#'s ToString is not implemented, it still overrides GDExtension's _to_string #94213

Open NoctemCat opened 2 months ago

NoctemCat commented 2 months ago

Tested versions

v4.2.2.stable.mono.official [15073afe3]

System information

Godot v4.2.2.stable.mono - Windows 10.0.19045 - Vulkan (Forward+) - dedicated NVIDIA GeForce GTX 1650 (NVIDIA; 32.0.15.5612) - Intel(R) Core(TM) i5-9300H CPU @ 2.40GHz (8 Threads)

Issue description

I was testing with overriding _to_string method in GDExtension class and when I attached C# script on that node, GDExtension's _to_string stopped working. ToString in C# was not overriden. When I tested the same script, but in GDScript it worked as expected, GDExtension's _to_string got called and printed. C# GetNode also can't access GDExtension's _to_string

I expect GDExtension _to_string to get called when C# script does not override it. At least to stop it from overriding C++ side image

Steps to reproduce

  1. Create GDExtension class with overriden _to_string
  2. Attach C# script to it (it needs to extend the closest to GDExtension Godot's class)
  3. Try to pass itself to GD.Print, or try to use to_string from GDExtension side, it now doesn't work

Minimal reproduction project (MRP)

to_string_repro.zip

raulsntos commented 1 month ago

If the GodotObject has a script attached (doesn't matter if it's GDScript or C#), the script's to_string method always takes precedence:

https://github.com/godotengine/godot/blob/54c5cdbbc8a9bef57fdad5942c20917ea169853c/core/object/object.cpp#L892-L908

In the case of a C# script, we end up calling CSharpInstanceBridge.CallToString:

https://github.com/godotengine/godot/blob/54c5cdbbc8a9bef57fdad5942c20917ea169853c/modules/mono/glue/GodotSharp/GodotSharp/Core/Bridge/CSharpInstanceBridge.cs#L151

This method calls System.Object.ToString which, if not overridden, falls back to the base implementation in GodotObject:

https://github.com/godotengine/godot/blob/54c5cdbbc8a9bef57fdad5942c20917ea169853c/modules/mono/glue/GodotSharp/GodotSharp/Core/GodotObject.base.cs#L143-L148

https://github.com/godotengine/godot/blob/54c5cdbbc8a9bef57fdad5942c20917ea169853c/modules/mono/glue/runtime_interop.cpp#L1429-L1437

NoctemCat commented 1 month ago

Then I guess the question is, should it set outValid to false if it is not being overriden in your class, similar to GDScript?

Also, I'm guessing this is also why this doesn't work? image

raulsntos commented 1 month ago

Checking if the C# user class implements/overrides a certain method, which is akin to duck typing, feels like the wrong approach for C#.

Maybe we can change godotsharp_object_to_string to match the Object::to_string implementation, and check if the Object has an extension. I've opened https://github.com/godotengine/godot/pull/94665 to explore this approach.