C# Abstract methods with default parameters

I wrote an abstract class CompositeConnector declaring abstract methods. One of them, DrawContent has a default string parameter.

public abstract class CompositeConnector : ScriptableObject
{
// ... 
    public abstract void DrawContent(GUISkin skin, float zoom_factor, string control_name = "");
// ...
}

Other classes inheriting from CompositeConnector also declare a default parameter.

public class InputTextConnector : CompositeConnector
{
// ...
    public override void DrawContent(GUISkin skin, float zoom_factor, string control_name = "")
    {
    // ...
    }
// ...
}

My problem comes when I call this method without the latest parameter.

// Produces the error.
_input_connector.DrawContent(_skin, _zoom_factor);
// No error.
_input_connector.DrawContent(_skin, _zoom_factor, "");

I get an error such as (not at runtime):

Unhandled Exception: System.ArgumentException: Key duplication when
adding: Void DrawContent(UnityEngine.GUISkin, Single, System.String)

As you can see if I set the parameter and don’t rely with the default value I don’t get the error. I tried a lot of various combination but the only solution I found was to not use default parameters.

Is this a known issue? Am I trying to do something wrong here? As it seems to be valid in C# is this coming from Unity?

Well, i don’t get that error in Unity 5.2.1f1. Are you sure:

  • you have the InputTextConnector class in a seperate file called “InputTextConnector.cs”?
  • you create an instance with the CreateInstance method of ScriptableObject?

Apart from that i don’t see any reason why specifying two default parameters. Default parameters are just syntactic sugar and are actually implemented different depending on the compiler.

For example if you declare your methods like this;

public abstract void DrawContent(GUISkin skin, float zoom_factor, string control_name = "base");
// ...
public override void DrawContent(GUISkin skin, float zoom_factor, string control_name = "derived")
{
    Debug.Log("DrawContent: " + zoom_factor + ", " + control_name);
}

You probably can’t guess what the result is in the following cases:

    // in Unity
    InputTextConnector i = ScriptableObject.CreateInstance<InputTextConnector>();
    CompositeConnector c = i;
    i.DrawContent(null, 0f);
    c.DrawContent(null, 0f);

In Unity you will get two times

"DrawContent: 0, base"
"DrawContent: 0, base"

Doing the same thing in normal C# (of course removing the ScriptableObject inheritance)

    InputTextConnector i = new InputTextConnector();
    CompositeConnector c = i;
    i.DrawContent(null, 0f);
    c.DrawContent(null, 0f);

In a C# console applciation we get this:

"DrawContent: 0, derived"
"DrawContent: 0, base"

Default parameters are just syntactic sugar. The compiler literally adds the default parameter to the call. Now it depends on how the compiler determines the default parameter. C# clearly uses the variable type while Unity’s Mono compiler uses the “first declared” default value.

So it’s quite dangerous to rely on different default values for different derived classes. You should only specify one default value for the same parameter.