🔍 Know Which Argument Caused the Error
ArgumentNullException with parameter name is good. CallerArgumentExpression shows the actual expression. Even better for complex validations.
❌ Standard Validation
public void Process(string data)
{
if (data == null)
throw new ArgumentNullException(nameof(data));
}
// Exception: Parameter name: data
✅ CallerArgumentExpression
static void GuardNotNull(object value,
[CallerArgumentExpression("value")] string expr = null)
{
if (value is null)
throw new ArgumentNullException(expr);
}
// Usage
GuardNotNull(user.Address.City);
// Exception: Parameter name: user.Address.City
📝 Advanced Validation Helper
public static class Guard
{
public static void AgainstNull(T value,
[CallerArgumentExpression("value")] string expr = null)
where T : class
{
if (value is null)
throw new ArgumentNullException(expr);
}
public static void AgainstNullOrEmpty(string value,
[CallerArgumentExpression("value")] string expr = null)
{
if (string.IsNullOrEmpty(value))
throw new ArgumentException($"{expr} cannot be null or empty", expr);
}
public static void AgainstCondition(bool condition, string message,
[CallerArgumentExpression("condition")] string expr = null)
{
if (condition)
throw new ArgumentException($"{expr} is true: {message}", expr);
}
}
// Usage
Guard.AgainstNull(user.Address);
Guard.AgainstNullOrEmpty(user.Name);
Guard.AgainstCondition(age < 0, "Age cannot be negative", nameof(age));
💡 Benefits
- Shows exact expression that failed (not just parameter name)
- Great for complex nested properties
- Reduces boilerplate validation code
- Better error messages for debugging
"Null check on user.Address.City threw 'Value cannot be null. Parameter name: value'. Useless. CallerArgumentExpression shows 'user.Address.City'. Debugging time cut in half."
