Thursday, December 6, 2012

[C#] Generics: Testing what default(T) returns

default(T) seems to accept all types which originate from object.
It's said that it returns null for all reference types, and 0 for all value types. Let's see about that.

Reference Types
Value Types

Some code to get you started (Visual Studio):

Let's make about the simplest class possible to get the result of default(T) to a Console.WriteLine()
public class Test<T>
    private T defaultData;

    public Test()
        defaultData = default(T);
    public String GetDefaultData()
        return ((bool)object.Equals(defaultData, null) ? "null" : defaultData.ToString());
The GetDefaultData method exists because defaultData will result to null in some cases, and we want to print the result to the Console (which only takes strings / char-arrays).
As we cannot call ToString() on null because it's not a real object, we have to use object.Equal, which is a static method of the static class object. It compares two objects to see if they are equal.
(Technically null is an object, but it refers to to the location of no object, so no methods can be called upon it.)

If you are confused about the code after, it's called the "if else shorthand" or "conditional operator". You read it as the following:

condition ? condition_is_true : condition_is_false

Thus we return the string "null" if the defaultData is equal to the object null, so we can print it to the console. Otherwise, we can safely use ToString since the result is not null.

We can then test our class.
struct TestStruct {
    int x;

enum TestEnum {
    x = 1, y = 2

delegate void TestDelegate();

interface TestInterface {}

static void Main(string[] args) {

    var structT = new Test < TestStruct > ();
    var enumT = new Test < TestEnum > ();
    var longT = new Test < long > ();
    var intT = new Test < int > ();
    var boolT = new Test < bool > ();
    var stringT = new Test < String > ();
    var classT = new Test < Class1 > ();
    var dynamicT = new Test < dynamic > ();
    var objectT = new Test < object > ();
    var delegateT = new Test < TestDelegate > ();
    var interfaceT = new Test < TestInterface > ();

    Console.WriteLine("default data for the following types");
    Console.WriteLine("value types");
    Console.WriteLine("struct: " + structT.GetDefaultData());
    Console.WriteLine("enum: " + enumT.GetDefaultData());
    Console.WriteLine("long: " + longT.GetDefaultData());
    Console.WriteLine("int: " + intT.GetDefaultData());
    Console.WriteLine("bool: " + boolT.GetDefaultData());
    Console.WriteLine("reference types");
    Console.WriteLine("string: " + stringT.GetDefaultData());
    Console.WriteLine("class: " + classT.GetDefaultData());
    Console.WriteLine("dynamic: " + dynamicT.GetDefaultData());
    Console.WriteLine("object: " + objectT.GetDefaultData());
    Console.WriteLine("delegate: " + delegateT.GetDefaultData());
    Console.WriteLine("interface: " + interfaceT.GetDefaultData());


I'm not testing all the variations of int, float and decimal value types, because I'm fairly certain of that they all return 0.
And the results are:

We see now that bool, which is a value type, returns False instead of 0 (makes sense...). And struct returns something weird (Main() lies in Program.cs in the ConsoleApplication1 namespace.)

If you are dissatisfied with the results, I have bad news. It is impossible to override default(T).

No comments:

Post a Comment