C# to GDScript typed Array interop

Godot Version

Godot_v4.6-stable_mono

Question

I am writing a wrapper between C# and GDScript and attempting to pass an array of content from C# to GDScript. When the array in GDScript has a custom type it fails:

func set_array(items:Array[custom_class])
     #do work
public void SetArray(Godot.Collections.Array items)
{
    ExternalGDScriptObject.Call("set_array", items);
}
// Fails to run GDScript function with the error 
//'(Array) does not have the same element type as the
// expected typed array argument.'

If I remove the array type in the GDScript function, it will work.

func set_array(items:Array)
     #do work
#runs the function normally

This is not a problem for me but I am attempting to create the wrapper in a form usable for the public so the less the user of my wrapper needs to modify out of the base GDScript files, the better.

Since C# has no notion of GDScript classes, I was wondering if there was a way to arbitrarily type the C# array before passing it through or if removing the array type in the GDScript file is the way to go.

As a note, this only seems to effect innumerable (arrays, dictionaries, etc), single variables don’t seem to care what ‘type’ of object is passed which is what made me think maybe there was a way around it.

func set_entry(item:[custom_class])
#doesn't need a matching type in C#. Will still 
#fail if the object it needs is incorrect but doesn't
#abort the call if sent a GodotObject or Resource, or whatever.

Cheers!

My first guess would be that GDScript is expecting Array[Variant], and when you pass anything else the type doesn’t match.

The bigger question is why are you doing this?

If you need a data collection either do it in C# or in GDScript. I cant see any reason as to why you would want to do this.

1 Like

Mmmn, good idea but no dice. Tried Array<Variant> and it still rejects it. I’ve also tried a basic untyped Array with no luck. I have to assume since the internals of GDScript innumerables are untyped (i.e. you can have an array full of any variety of variant types you want), GDS is doing some sort of lazy compare between the sent ‘type’ and its designated ‘type’ and and descendants, then rejects the update if they they don’t match.

Interestingly it does seem to boil it down in some cases. Like, if the GDS array is typed to enums and I pass an array of ints, it doesn’t reject it:

func use_this(array_of_enums:Array[CustomEnums]) -> void:
	# Do work
public void SendEnums(List<EEnumTypes> enumTypes)
    {
        Array<int> convertedArray = [];
        foreach (var e in enumTypes)
        {
            convertedArray.Add((int)e);
        }
        
        GDScriptObject.Call("use_this", convertedArray);
    }
// Is not rejected.

At this point I guess I was just hoping there was some ‘catch all’ type that could be used to bypass whatever check GDS is doing.

1 Like

Its a wrapper. The add-on I’m wrapping is a complex GDScript process which does all the work but the C# user can’t communicate with it directly without C# typed wrapped objects (or writing a unique get/set/call request with every invocation). Thus, any GDS object they would want to interact with is ‘wrapped’ in a C# typed class and appropriately translated calls.

The C# user will create C#-typed wrapper objects to hold onto and work with (completely ignoring the GDS objects). These contain references to the add-on’s GodotObjects (usually Resources). And, when they need to communicate with the GDS portion, the wrapper passes its referenced object to the GDS function/variable.

And, circling back around to the problem: when these reference objects are passed in a collection, GDS rejects it if the type passed is not of the reference type or descendant. From a programmatic perspective, this makes perfect sense, but since there is no type between C# and GDS that can be compared, it makes it more difficult to create an interop layer.

Thus I was kinda hoping there was a way to ‘type as string’ or ‘pass as an any type’ or something that wouldn’t require the user to make changes to the base GDS add-on :slight_smile: