diff --git a/modules/mono/csharp_script.cpp b/modules/mono/csharp_script.cpp
index 7f92a672d46..50a78e379a2 100644
--- a/modules/mono/csharp_script.cpp
+++ b/modules/mono/csharp_script.cpp
@@ -41,6 +41,7 @@
 #include "editor/csharp_project.h"
 #include "editor/editor_node.h"
 #include "editor/godotsharp_editor.h"
+#include "utils/string_utils.h"
 #endif
 
 #include "godotsharp_dirs.h"
@@ -295,20 +296,88 @@ bool CSharpLanguage::has_named_classes() const {
 	return true;
 }
 
-String CSharpLanguage::make_function(const String &p_class, const String &p_name, const PoolStringArray &p_args) const {
+static String variant_type_to_managed_name(const String &p_var_type_name) {
 
+	if (p_var_type_name.empty())
+		return "object";
+
+	if (!ClassDB::class_exists(p_var_type_name)) {
+		Variant::Type var_types[] = {
+			Variant::BOOL,
+			Variant::INT,
+			Variant::REAL,
+			Variant::STRING,
+			Variant::VECTOR2,
+			Variant::RECT2,
+			Variant::VECTOR3,
+			Variant::TRANSFORM2D,
+			Variant::PLANE,
+			Variant::QUAT,
+			Variant::RECT3,
+			Variant::BASIS,
+			Variant::TRANSFORM,
+			Variant::COLOR,
+			Variant::NODE_PATH,
+			Variant::_RID
+		};
+
+		for (int i = 0; i < sizeof(var_types) / sizeof(Variant::Type); i++) {
+			if (p_var_type_name == Variant::get_type_name(var_types[i]))
+				return p_var_type_name;
+		}
+
+		if (p_var_type_name == "String")
+			return "string"; // I prefer this one >:[
+
+		// TODO these will be rewritten later into custom containers
+
+		if (p_var_type_name == "Array")
+			return "object[]";
+
+		if (p_var_type_name == "Dictionary")
+			return "Dictionary<object, object>";
+
+		if (p_var_type_name == "PoolByteArray")
+			return "byte[]";
+		if (p_var_type_name == "PoolIntArray")
+			return "int[]";
+		if (p_var_type_name == "PoolRealArray")
+			return "float[]";
+		if (p_var_type_name == "PoolStringArray")
+			return "string[]";
+		if (p_var_type_name == "PoolVector2Array")
+			return "Vector2[]";
+		if (p_var_type_name == "PoolVector3Array")
+			return "Vector3[]";
+		if (p_var_type_name == "PoolColorArray")
+			return "Color[]";
+
+		return "object";
+	}
+
+	return p_var_type_name;
+}
+
+String CSharpLanguage::make_function(const String &p_class, const String &p_name, const PoolStringArray &p_args) const {
+#ifdef TOOLS_ENABLED
 	// FIXME
-	// Due to Godot's API limitation this just appends the function to the end of the file
-	// Another limitation is that the parameter types are not specified, so we must use System.Object
+	// - Due to Godot's API limitation this just appends the function to the end of the file
+	// - Use fully qualified name if there is ambiguity
 	String s = "private void " + p_name + "(";
 	for (int i = 0; i < p_args.size(); i++) {
+		const String &arg = p_args[i];
+
 		if (i > 0)
 			s += ", ";
-		s += "object " + p_args[i];
+
+		s += variant_type_to_managed_name(arg.get_slice(":", 1)) + " " + escape_csharp_keyword(arg.get_slice(":", 0));
 	}
 	s += ")\n{\n    // Replace with function body\n}\n";
 
 	return s;
+#else
+	return String();
+#endif
 }
 
 void CSharpLanguage::frame() {
diff --git a/modules/mono/editor/bindings_generator.cpp b/modules/mono/editor/bindings_generator.cpp
index c122795fcec..95e75f91031 100644
--- a/modules/mono/editor/bindings_generator.cpp
+++ b/modules/mono/editor/bindings_generator.cpp
@@ -108,36 +108,6 @@ const char *BindingsGenerator::TypeInterface::DEFAULT_VARARG_C_IN = "\t%0 %1_in
 
 bool BindingsGenerator::verbose_output = false;
 
-static bool is_csharp_keyword(const String &p_name) {
-
-	// Reserved keywords
-
-	return p_name == "abstract" || p_name == "as" || p_name == "base" || p_name == "bool" ||
-		   p_name == "break" || p_name == "byte" || p_name == "case" || p_name == "catch" ||
-		   p_name == "char" || p_name == "checked" || p_name == "class" || p_name == "const" ||
-		   p_name == "continue" || p_name == "decimal" || p_name == "default" || p_name == "delegate" ||
-		   p_name == "do" || p_name == "double" || p_name == "else" || p_name == "enum" ||
-		   p_name == "event" || p_name == "explicit" || p_name == "extern" || p_name == "false" ||
-		   p_name == "finally" || p_name == "fixed" || p_name == "float" || p_name == "for" ||
-		   p_name == "forech" || p_name == "goto" || p_name == "if" || p_name == "implicit" ||
-		   p_name == "in" || p_name == "int" || p_name == "interface" || p_name == "internal" ||
-		   p_name == "is" || p_name == "lock" || p_name == "long" || p_name == "namespace" ||
-		   p_name == "new" || p_name == "null" || p_name == "object" || p_name == "operator" ||
-		   p_name == "out" || p_name == "override" || p_name == "params" || p_name == "private" ||
-		   p_name == "protected" || p_name == "public" || p_name == "readonly" || p_name == "ref" ||
-		   p_name == "return" || p_name == "sbyte" || p_name == "sealed" || p_name == "short" ||
-		   p_name == "sizeof" || p_name == "stackalloc" || p_name == "static" || p_name == "string" ||
-		   p_name == "struct" || p_name == "switch" || p_name == "this" || p_name == "throw" ||
-		   p_name == "true" || p_name == "try" || p_name == "typeof" || p_name == "uint" || p_name == "ulong" ||
-		   p_name == "unchecked" || p_name == "unsafe" || p_name == "ushort" || p_name == "using" ||
-		   p_name == "virtual" || p_name == "volatile" || p_name == "void" || p_name == "while";
-}
-
-inline static String escape_csharp_keyword(const String &p_name) {
-
-	return is_csharp_keyword(p_name) ? "@" + p_name : p_name;
-}
-
 static String snake_to_pascal_case(const String &p_identifier) {
 
 	String ret;
diff --git a/modules/mono/utils/string_utils.cpp b/modules/mono/utils/string_utils.cpp
index de1a60dbd14..f26663ea110 100644
--- a/modules/mono/utils/string_utils.cpp
+++ b/modules/mono/utils/string_utils.cpp
@@ -126,3 +126,32 @@ String sformat(const String &p_text, const Variant &p1, const Variant &p2, const
 
 	return new_string;
 }
+
+bool is_csharp_keyword(const String &p_name) {
+
+	// Reserved keywords
+
+	return p_name == "abstract" || p_name == "as" || p_name == "base" || p_name == "bool" ||
+		   p_name == "break" || p_name == "byte" || p_name == "case" || p_name == "catch" ||
+		   p_name == "char" || p_name == "checked" || p_name == "class" || p_name == "const" ||
+		   p_name == "continue" || p_name == "decimal" || p_name == "default" || p_name == "delegate" ||
+		   p_name == "do" || p_name == "double" || p_name == "else" || p_name == "enum" ||
+		   p_name == "event" || p_name == "explicit" || p_name == "extern" || p_name == "false" ||
+		   p_name == "finally" || p_name == "fixed" || p_name == "float" || p_name == "for" ||
+		   p_name == "forech" || p_name == "goto" || p_name == "if" || p_name == "implicit" ||
+		   p_name == "in" || p_name == "int" || p_name == "interface" || p_name == "internal" ||
+		   p_name == "is" || p_name == "lock" || p_name == "long" || p_name == "namespace" ||
+		   p_name == "new" || p_name == "null" || p_name == "object" || p_name == "operator" ||
+		   p_name == "out" || p_name == "override" || p_name == "params" || p_name == "private" ||
+		   p_name == "protected" || p_name == "public" || p_name == "readonly" || p_name == "ref" ||
+		   p_name == "return" || p_name == "sbyte" || p_name == "sealed" || p_name == "short" ||
+		   p_name == "sizeof" || p_name == "stackalloc" || p_name == "static" || p_name == "string" ||
+		   p_name == "struct" || p_name == "switch" || p_name == "this" || p_name == "throw" ||
+		   p_name == "true" || p_name == "try" || p_name == "typeof" || p_name == "uint" || p_name == "ulong" ||
+		   p_name == "unchecked" || p_name == "unsafe" || p_name == "ushort" || p_name == "using" ||
+		   p_name == "virtual" || p_name == "volatile" || p_name == "void" || p_name == "while";
+}
+
+String escape_csharp_keyword(const String &p_name) {
+	return is_csharp_keyword(p_name) ? "@" + p_name : p_name;
+}
diff --git a/modules/mono/utils/string_utils.h b/modules/mono/utils/string_utils.h
index 2f2c3c2d899..a0d66ebdc30 100644
--- a/modules/mono/utils/string_utils.h
+++ b/modules/mono/utils/string_utils.h
@@ -35,4 +35,10 @@
 
 String sformat(const String &p_text, const Variant &p1 = Variant(), const Variant &p2 = Variant(), const Variant &p3 = Variant(), const Variant &p4 = Variant(), const Variant &p5 = Variant());
 
+#ifdef TOOLS_ENABLED
+bool is_csharp_keyword(const String &p_name);
+
+String escape_csharp_keyword(const String &p_name);
+#endif
+
 #endif // STRING_FORMAT_H