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
forbool
\0
forchar
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:
- Traditional syntax:
default(T)
where T is the type - C# 7.1 and later: Just
default
when the compiler can infer the type
Examples of Default Keyword
Basic Usage
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
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:
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:
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:
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:
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:
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
-
Create a generic method called
IsDefault<T>(T value)
that checks if a given value equals the default value for its type. -
Implement a generic
Stack<T>
class that uses thedefault
keyword to initialize elements and check for empty conditions. -
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! :)