17
votes

I've run into a really strange problem when using the default keyword in a DLL project. In my DLL project (compiled with VS2013) I have the following class:

public class BaseClass<T>
{
    public T value;
    public bool enabled;

    public BaseClass ( T value = default(T), bool enabled = true )
    {
        this.value = value;
        this.enabled = enabled;
    }
}

Now, if I use this inside the DLL project, it works perfectly. I can create classes that derive from this base class without issue. But, as soon as I try to use the DLL in another project (compiled with Mono 2.0.0), deriving from the base class with a value type causes a compiler error. This:

public class ChildClass : BaseClass<int>
{
}

causes this:

Assets/ChildClass.cs(8,14): error CS1502: The best overloaded method match for BaseClass<int>.BaseClass(int, bool)' has some invalid arguments

Assets/ChildClass.cs(8,14): error CS1503: Argument #1' cannot convertnull' expression to type `int'

However, the base class with value types can be used in fields without an issue:

public class OtherClass
{
    public BaseClass<int> baseInt;
}

I looked at the DLL using ILSpy and noticed this:

public class BaseClass<T>
{
    public T value;
    public bool enabled;
    public BaseClass(T value = null, bool enabled = true)
    {
        this.value = value;
        this.enabled = enabled;
    }
}

Note that default<T> in the constructor has been replaced with null. This seems to be the cause of the problem, as null would be an invalid value for a value type.

So what's going on here?

EDIT: As discovered in the comments, this doesn't occur when the second project is compiled with VS2013, or with newer versions of Mono.

1
This is a compiler bug. What compiler is it? - usr
I just tried this with VS 2013 and it works fine. - Mike Zboray
Tested on .NET 4.0 in VS 2012, compiled as X86 - works fine too - user2126375
Interesting. The DLL is created with VS, but the project that uses it is using Mono. I just tested with a VS project and it works fine. The question is, is it Mono or VS that's wrong? VS is compiling the default(T) to null but then still treating it as default(T) when using the DLL. Mono seems to be using null since that's what's actually there. - Adam
Maybe VS is not actually compiling to null but to "default". Maybe the tools just show this in the wrong way. You could look into the CLS spec what the assembly meta-data is actually capable of storing. - usr

1 Answers

5
votes

This seems to be a bug with the mono compiler pre-3.2.3 (@usr was quite right in their initial comment). The compiler inserts default parameter values into the assembly metadata as attributes (see this answer). I verified the output of ilspy is consistent with ildasm which encodes the default(T) to .param [1] = nullref. I suspect the convention is that a generic default(T) is encoded as null and the consuming compiler is just supposed to know how to use that. It seems related to this issue, however, based on the dates, this particular issue was fixed some time before that was reported.