Back to Blog
C#

c# property default value: Setting Initial Values Correctly

c# property default value: Learn how to set default values for C# properties using initializers, constructors, and backing fields, and understand the runtime behavior...

C#property initializerconstructorbacking fieldauto-property
Diagram showing a C# property with multiple default value assignment options: initializer, constructor, and backing field.

When you declare a property in C#, its default value is determined by how you define it. An auto-property without an initializer gets the default value of its type—null for reference types, zero for numeric types, false for bool, and so on. But that default is often not what you want in practice. You might need a non-null collection, a specific numeric starting point, or a configuration value that should be present from the moment the object is created. Setting a c# property default value can be done in several ways, and the choice affects readability, runtime behavior, and how the object can be constructed.

What Happens Without an Explicit Default

Consider a simple auto-property:

public class Order { public int Quantity { get; set; } public string CustomerName { get; set; } public List<string> Items { get; set; } }

Quantity defaults to 0, CustomerName to null, and Items to null. If you later try to add to Items without checking, you get a NullReferenceException. This is the most common reason developers need to set a default value explicitly. The default value of a property is the same as the default value of its underlying type unless you override it.

Using Property Initializers (C# 6 and Later)

C# 6 introduced auto-property initializers, which let you assign a value directly at the declaration site:

public class Order { public int Quantity { get; set; } = 1; public string CustomerName { get; set; } = "Unknown"; public List<string> Items { get; set; } = new List<string>(); }

This is the most concise way to set a default value for an auto-property. The initializer runs when the object is constructed, before the constructor body executes. That means you can rely on the property having that value even before the constructor assigns anything else. Property initializers also work for static properties:

public static int MaxRetries { get; set; } = 3;

The initializer runs once when the type is initialized, not per instance.

Setting Defaults in the Constructor

Before C# 6, the common approach was to assign values in the constructor. This still works and is sometimes necessary when the default value depends on constructor parameters or other initialization logic:

public class Order { public int Quantity { get; set; } public string CustomerName { get; set; } public List<string> Items { get; set; } public Order() { Quantity = 1; CustomerName = "Unknown"; Items = new List<string>(); } }

Constructor assignment gives you more flexibility: you can compute the value, call a method, or pass a parameter to the constructor and use it to set the property. However, it separates the default from the declaration, which can make the code harder to read when there are many properties. Also, if you add a new property and forget to assign it in the constructor, it silently falls back to the type's default.

Backing Field Initializers for Manual Properties

If you are using a manual property with an explicit backing field, you can initialize the field directly:

private int _quantity = 1; public int Quantity { get { return _quantity; } set { _quantity = value; } }

The field initializer runs before the constructor body, just like property initializers. This is useful when the property's getter or setter contains logic that should not be bypassed. For example, you might want to validate the value in the setter, but still have a valid starting point. The field initializer ensures the backing field is set before any property access.

Choosing Between Initializers and Constructor Assignment

The decision often comes down to whether the default value is static or depends on construction context. Use a property initializer when the default is a constant or a simple new object that does not depend on other state. Use constructor assignment when the default value must be computed from constructor parameters, or when you need to call a method that requires the object to be fully constructed. There is also a subtle difference in execution order: property initializers run before the base constructor call, while constructor body assignments run after. For most scenarios this does not matter, but if you have a base class that invokes virtual members during construction, the timing can affect behavior.

Runtime Behavior and Compatibility

Property initializers are compiled into the constructor. For each instance property with an initializer, the assignment is inserted at the beginning of the constructor, after the call to the base constructor but before the constructor body. This means the initializer runs even if the constructor throws later, which is usually desirable. For static properties, the initializer runs as part of the static constructor, which is triggered on first access to the type. This can have implications for type initialization order, especially if you have static properties that depend on each other.

Another consideration is object initializer syntax. When you use new Order { Quantity = 2 }, the property initializer runs first, then the object initializer overrides it. That is expected, but it means the default value is not a constant—it is a starting point that can be replaced. If you need a property that can only be set once, you might use a read-only property with a backing field and set it in the constructor, but that is a different pattern.

When Default Values Affect Serialization and Equality

Default values also matter when you serialize objects to JSON or XML. If a property has a default value, some serializers might omit it or include it depending on configuration. For example, System.Text.Json includes all public properties by default, but you can use [JsonIgnore] or [JsonPropertyName] to control behavior. Similarly, when implementing Equals, you often compare property values, and the default value becomes part of the equality contract. If you change the default value, existing serialized data or equality checks might break. This is a maintainability concern: choose a default that is stable and intentional.

A Practical Example: Configuration Object

Suppose you have a class that holds application settings:

public class AppConfig { public string LogLevel { get; set; } = "Information"; public int MaxConnections { get; set; } = 10; public TimeSpan Timeout { get; set; } = TimeSpan.FromSeconds(30); }

These defaults are reasonable for a typical application. If you later decide the timeout should be 60 seconds, you change the initializer. But if the configuration is loaded from a file, you might want to distinguish between a value that was explicitly set and one that came from the default. In that case, you might use nullable properties and treat null as "not set", then apply defaults during loading. That is a different pattern, but it shows that the choice of default value strategy is tied to how the object is used.

Compatibility with Older C# Versions

If you are working on a codebase that targets an older C# version, property initializers are not available. The constructor approach works everywhere. If you need to support both, you can use a helper method that assigns defaults and call it from the constructor, but that adds indirection. In modern C#, property initializers are the standard for simple defaults, and they are widely understood. The main risk is that developers new to C# might not realize that an auto-property without an initializer does not have a meaningful default for reference types—it is null. The best practice is to always initialize reference-type properties to a non-null value when that value is known, unless null is a valid state.

Performance and Memory Considerations

Property initializers do not add runtime overhead beyond the assignment itself. The compiler generates the same IL as if you had assigned the value in the constructor. For value types, the default value is often the zero-bit pattern, and setting a different value requires a write. For reference types, initializing to a new object allocates memory, which is unavoidable if you need a non-null collection. There is no performance reason to avoid property initializers; the cost is identical to manual assignment. However, if you have a property that is rarely used, you might want to initialize it lazily to avoid allocation. That is a separate optimization and should be measured before applying.

Final Section: Order of Initialization and Inheritance

When a class inherits from a base class, property initializers in the derived class run after the base constructor has completed. This is because the derived constructor calls the base constructor first, and then the derived property initializers are executed as part of the derived constructor's beginning. This order can cause issues if the base constructor calls a virtual method that accesses a derived property. The derived property still has its default value at that point, not the initializer value. To avoid this, avoid calling virtual members from constructors, or use a different initialization pattern. This is a subtle but important runtime behavior that can lead to bugs if you assume property initializers run before any code in the base class. Understanding this ordering helps you decide whether to use property initializers or constructor assignment for properties that might be accessed during base construction.

c# property default value: How to Set Defaults in Properties | RYUSLOG DEV