BRO1104: Use default instead of a value type's default constructor¶
new int() creates the default value; default(int) says so. Replaces StyleCop SA1129.
Example¶
Before¶
using System;
using System.Threading;
public class Scheduler
{
public DateTime Start { get; set; } = new DateTime();
public Guid Run(CancellationToken token = new CancellationToken())
{
return new Guid();
}
}
After¶
using System;
using System.Threading;
public class Scheduler
{
public DateTime Start { get; set; } = default(DateTime);
public Guid Run(CancellationToken token = default(CancellationToken))
{
return Guid.Empty;
}
}
Why¶
new DateTime() looks like it creates something meaningful, but it's just DateTime.MinValue, the all-zero default.
Writing default(DateTime), or Guid.Empty, makes that explicit.
How the fix works¶
The same replacements as StyleCop:
| Before | After |
|---|---|
new int(), new Point(), Point p = new(); |
default(int), default(Point), default(Point) |
new CancellationToken() |
CancellationToken.None |
new Guid() |
Guid.Empty |
new IntPtr(), new UIntPtr() |
IntPtr.Zero, UIntPtr.Zero |
new nint(), new nuint() |
default(nint), default(nuint) |
new Color() for an enum with a zero member Red |
Color.Red |
A parameter's default value must be a constant, so there new Guid() becomes default(Guid) instead of
Guid.Empty, as in the example.
Configuration¶
The SDK's csharp_prefer_simple_default_expression (IDE0034's option) is followed when it is set to true (any
severity suffix, such as true:warning): the fix writes the default literal instead of default(T). Unset or
false keeps default(T), like StyleCop. The named replacements (Guid.Empty, CancellationToken.None,
IntPtr.Zero, an enum's zero member) stay as they are.
[*.cs]
csharp_prefer_simple_default_expression = true
// Before
public DateTime Start { get; set; } = new DateTime();
public void Run(CancellationToken token = new CancellationToken()) { }
var count = new int();
// After
public DateTime Start { get; set; } = default;
public void Run(CancellationToken token = default) { }
var count = default(int);
The literal is written only where it means the same, checked by binding the statement again with it: var has no
type for it, a conversion would change the value (object o = new int() boxes a zero, object o = default is null;
int? n = default is null), an overload may no longer be picked, and a member access on it doesn't compile. Those keep
default(T). All creations and default(T) expressions of one statement, initializer or expression body are checked
together, since each alone may bind where all together don't (M(default, default) can be ambiguous). The literal
needs C# 7.1; in a multi-targeted project the lowest language version of its target frameworks counts. With IDE0034 on
as well, dotnet format converges in one run: before, new T() became default(T) and IDE0034 needed a second run to
simplify it.
Not reported¶
- Structs with their own parameterless constructor (C# 10 and later).
new S()runs that constructor;default(S)doesn't. Like StyleCop. new T()for a type parameter (where T : struct). IfThas a parameterless constructor,new T()runs it anddefault(T)doesn't.new S();as a statement on its own.default(S);doesn't compile.- Creations with an initializer, even an empty one (
new Point() { }), like StyleCop. - Creations with a comment inside (
new Guid(/* none */)): the replacement would drop it.
Compared with StyleCop¶
nint/nuint always become default(nint)/default(nuint). StyleCop 1.2.0-beta.556 always writes nint.Zero, which
doesn't compile (CS0117) before C# 11 or on runtimes without numeric IntPtr (.NET Framework, .NET Standard,
.NET 6). StyleCop's master (unreleased) writes nint.Zero only where it compiles, but a multi-targeted project
analyzes each file once per target framework: for net462;net8.0 the two copies would get different fixes, and the
net462 build would break. default(nint) means the same and compiles everywhere. StyleCop also replaces new T() for type parameters, and turns new S(); into default(S);, which doesn't compile.