Udostępnij za pośrednictwem


C# 7 Series, Part 3: Default Literals

C#’s default keyword has two kinds of usages: one is to label the fallback branch of the switch…case structure (for any path that does not fall into the case conditions); another is to represent the “default” value of a type. I will discuss this usage in this blog.

The default value of a type is either:

  • null, if the type is a reference type, or
  • an instance of the type constructed with the parameterless constructor, if the type is a value type.

The following screenshot is a quick example.

image

However, you can not have a default expression for any open type (Note: An open type is a type that has unbound type arguments; a type has all bound type arguments is called closed type), for example:

image

Now, if we want to use default value for a generic type in a method, we have to write code like this:

 public static void Method(ImmutableArray<int> array) { }
 
public static void Main(string[] args)
{     
    Method(default(ImmutableArray<int>));
}

Do you see a redundant here? the compiler should know the exact type needed for the Method() for the first parameter. A default literal without the type specification should be sufficient enough.

Default Literals

C# 7.1 allows the default expressions. If compile with the C# language version 7.1, or latest, you can simplify the code as the following:

 public static void Method(ImmutableArray<int> array) { }
public static void Main(string[] args)
{ 
    Method(default);
}

Now you get a clean code!

Similarly, you can have default literals for all the place that default(…) can appear:

 public static void Main(string[] args = default)    // Default value for the optional parameter        {
    int i = default;        // Default value (0) for type System.Int32 
    string s = default;     // Default value (null) for type System.String 
    Method(default);        // Calling a method with default value for an argument 
    T t = default;          // Default value for a type parameter 
    return default;         // Default return value for a method that does not return void 
}

You can have default literals in a condition test:

 int x = 2; 
if (x == default) { }       // Test if x is default value (0) of the type System.Int32 
if (x is default) { }       // Same as above

More interestingly, you can use default literals in switch…case. Oh, cool! but… wait, how about the label ‘case default’? Here is what I get from the IDE:

image

And if you apply the fix, it will be:

 int x = 2; 
switch (x) 
{ 
    case (default): break; 
    default: break; 
}

This looks confusing, but at least we get live analytics about this, Thanks Roslyn!

The last thing I want to call out is there are cases that cannot apply default literals, for example:

 // Error: 'as' must have reference type 
default as int; 
// OK. But the left hand expression is always null.
if (default as string == string.Empty) { }
// Error. Cannot apply operator 'is' to type 'default'.
if (default is string) { }

Conclusion

Default literals in C# 7.1 avoids redundant typing where the default value is known to compiler. This is a syntactical improvement and nothing changed for the CLR, The new code is 100% compatible with the code built by earlier versions of the C# compiler.

Comments

  • Anonymous
    June 06, 2017
    How do you differentiate between 'case default' and 'default' in goto statements? Would it be 'goto (default)' vs 'goto default'?
    • Anonymous
      June 06, 2017
      Ah it would be 'goto case default', doh! Sorry Mark.
    • Anonymous
      June 12, 2017
      If you need use "goto" statement in your code that means something goes terribly wrong
      • Anonymous
        June 16, 2017
        I would say it really depend on what problem needs to solve by "goto". There are cases such as switch-case that use 'fall through' requires "goto", e.g.switch (i){case 0: // direct fall-through without using "goto", as no body inside this "case".case 1: // something done here goto case 2; // we need "goto" here as there IS body.case 2: // do things break;default: break;}Of course you are saying other kinds of "goto" (from traditional C/C++ thought), unfortunately, I also saw some code from .NET Core that really requires this kind of goto in order to write less similar-to-same code.
  • Anonymous
    June 06, 2017
    What if you have multiple parameters in a method? In my opinion the default keyword is much less readable than having the type as parameter. For example:Method(default(ImmutableArray), default(ImmutableArray), default(ImmutableArray));vsMethod(default, default, default); // hard to remember which one is which?
    • Anonymous
      June 12, 2017
      In my opinion if you want to allow for this kind situation is better to introduce optional parameters:Method(ImmutableArray a = default, ImmutableArray b = default, ImmutableArray c = default)
    • Anonymous
      June 13, 2017
      This is true for any literal. I am working on a c# 7.2 feature to address this: an improvement to named arguments. You will be able to write M(ignoreEmpty: true, accumulator, cancellation: default);.It is more informative to specify the name of the argument, than the type in default.
  • Anonymous
    June 12, 2017
    why not a three letter word like "def" instead of "default" .. you don't use def anywhere in c# seriously "default" will creap in the code.. =) , even just underscore "_" would have been better than both.. return _ ;string[] args = _ ;
    • Anonymous
      June 14, 2017
      Many people, me included, will always choose readability over length of the code. There's no way I could choose cryptic special characters over plain English words.
  • Anonymous
    January 25, 2019
    The default literal is never constructed with the parameterless constructor.Most value types don't have a parameterless constructor.If you happen to find one, it is invoked only with a 'new' expression.
  • Anonymous
    May 22, 2019
    when redundant vs readable/maintainable, which one do you choose?