Skip to main content

C# Default Keyword

Introduction

When working with C# generics, you'll often need a way to provide a "default" value for a type parameter. The default keyword in C# serves exactly this purpose. It returns the default value for any type - whether that's a value type like int or a reference type like a class.

The default keyword is particularly useful when working with generic code where you don't know the specific type at compile time but need to provide an initial or fallback value.

Understanding the Default Keyword

The default keyword returns:

  • 0 for numeric value types (int, float, double, etc.)
  • false for bool
  • \0 for char
  • null for reference types (classes, interfaces, delegates)
  • null for nullable value types (int?, bool?, etc.)
  • Default values for each field in a struct

Basic Syntax

The default keyword can be used in two ways:

  1. Traditional syntax: default(T) where T is the type
  2. C# 7.1 and later: Just default when the compiler can infer the type

Examples of Default Keyword

Basic Usage

csharp
int defaultInt = default(int);      // 0
bool defaultBool = default(bool); // false
string defaultString = default(string); // null

// C# 7.1+ simplified syntax
int anotherInt = default; // 0
string anotherString = default; // null

Console.WriteLine($"Default int: {defaultInt}");
Console.WriteLine($"Default bool: {defaultBool}");
Console.WriteLine($"Default string: {defaultString ?? "null"}");

Output:

Default int: 0
Default bool: False
Default string: null

Using Default with Generic Methods

csharp
public static T GetDefaultValue<T>()
{
return default(T);
}

// Using the method
int defaultInt = GetDefaultValue<int>(); // Returns 0
string defaultStr = GetDefaultValue<string>(); // Returns null

Console.WriteLine($"Default int from generic method: {defaultInt}");
Console.WriteLine($"Default string from generic method: {defaultStr ?? "null"}");

Output:

Default int from generic method: 0
Default string from generic method: null

The Default Keyword in Generic Collections

When working with generic collections, the default keyword helps initialize or reset elements:

csharp
public class GenericRepository<T>
{
private T[] items = new T[100];

public void Reset(int index)
{
if (index >= 0 && index < items.Length)
{
items[index] = default(T);
Console.WriteLine($"Reset item at index {index} to default value");
}
}

public void SetItem(int index, T value)
{
if (index >= 0 && index < items.Length)
{
items[index] = value;
}
}

public T GetItem(int index)
{
if (index >= 0 && index < items.Length)
{
return items[index];
}
return default(T); // Return default value if index is out of range
}
}

Default Expressions with Nullables

The default keyword is useful when working with nullable types:

csharp
int? nullableInt = default(int?);  // null, not 0
bool? nullableBool = default; // null, not false

Console.WriteLine($"Default nullable int: {nullableInt?.ToString() ?? "null"}");
Console.WriteLine($"Default nullable bool: {nullableBool?.ToString() ?? "null"}");

Output:

Default nullable int: null
Default nullable bool: null

Practical Example: Generic Cache Implementation

Let's implement a simple generic cache that uses the default keyword to handle cache misses:

csharp
public class SimpleCache<TKey, TValue>
{
private Dictionary<TKey, TValue> cache = new Dictionary<TKey, TValue>();

public TValue GetValue(TKey key, Func<TKey, TValue> valueFactory)
{
if (cache.TryGetValue(key, out TValue value))
{
Console.WriteLine("Cache hit!");
return value;
}

Console.WriteLine("Cache miss! Computing value...");
// If key doesn't exist, use the valueFactory to create it
TValue newValue = valueFactory(key);

// If the factory returned default(TValue), don't cache it
if (!EqualityComparer<TValue>.Default.Equals(newValue, default(TValue)))
{
cache[key] = newValue;
}

return newValue;
}

public void Clear()
{
cache.Clear();
}
}

// Example usage
static void CacheExample()
{
var numberCache = new SimpleCache<string, int>();

// First access - cache miss
int result1 = numberCache.GetValue("one", key => {
// Calculate value based on key
return key == "one" ? 1 : 0;
});

// Second access - cache hit
int result2 = numberCache.GetValue("one", key => {
// This won't be executed due to cache hit
return 999;
});

Console.WriteLine($"First result: {result1}");
Console.WriteLine($"Second result: {result2}");
}

Output:

Cache miss! Computing value...
First result: 1
Cache hit!
Second result: 1

Using Default in Conditional Logic

The default keyword is helpful for initializing variables that might or might not get a value:

csharp
public static T ProcessData<T>(bool condition, T valueIfTrue)
{
T result = default;

if (condition)
{
result = valueIfTrue;
}

return result; // Returns valueIfTrue or default(T)
}

// Example usage
int processedInt = ProcessData(true, 42); // Returns 42
int defaultInt = ProcessData(false, 42); // Returns 0 (default)
string processedString = ProcessData(true, "Hello"); // Returns "Hello"
string defaultString = ProcessData(false, "Hello"); // Returns null (default)

Console.WriteLine($"Processed int: {processedInt}");
Console.WriteLine($"Default int: {defaultInt}");
Console.WriteLine($"Processed string: {processedString}");
Console.WriteLine($"Default string: {defaultString ?? "null"}");

Output:

Processed int: 42
Default int: 0
Processed string: Hello
Default string: null

Default with Custom Types

The default keyword works with your own custom types too:

csharp
public struct Point
{
public int X { get; set; }
public int Y { get; set; }

public override string ToString()
{
return $"({X}, {Y})";
}
}

public class Person
{
public string Name { get; set; }
public int Age { get; set; }

public override string ToString()
{
return $"Person: {Name}, Age: {Age}";
}
}

// Using default with custom types
Point defaultPoint = default;
Person defaultPerson = default;

Console.WriteLine($"Default Point: {defaultPoint}"); // (0, 0)
Console.WriteLine($"Default Person: {defaultPerson ?? "null"}"); // null

Output:

Default Point: (0, 0)
Default Person: null

Summary

The default keyword in C# is a powerful feature when working with generics and type parameters. It provides appropriate default values based on the type:

  • For value types (int, bool, etc.), it provides their zero/empty value (0, false)
  • For reference types, it provides null
  • For structs, it sets all fields to their default values

This makes generic programming much more flexible, as you can write code that works with any type without needing to know specific default values for each type.

Exercises

  1. Create a generic method called IsDefault<T>(T value) that checks if a given value equals the default value for its type.

  2. Implement a generic Stack<T> class that uses the default keyword to initialize elements and check for empty conditions.

  3. Write a generic TryGetValue<TKey, TValue> method that returns a boolean indicating success and outputs the value if found, or the default value if not found.

Additional Resources



If you spot any mistakes on this website, please let me know at [email protected]. I’d greatly appreciate your feedback! :)