skip to Main Content

I have struct named "NonNullableString". It throws exception in it’s constructor if the string value is null.

    public struct NonNullableString
    {
        public string Value { get; set; }

        private NonNullableString(string value)
        {
            if (value == null)
                throw new ArgumentNullException($"{value} cannot be null!", nameof(value));

            Value = value;
        }

        public static implicit operator NonNullableString(string value)
        {
            return new NonNullableString(value);
        }

         public static implicit operator string(NonNullableString value)
        {
            return value.Value;
        }
    }

I am using it in the classes where I throw exception if the property is given null. For example:

    public class User
    {
        public NonNullableString Username { get; set; }

        // ... other properties
        
        public User(string username)
        {
            Username = username;    
        }
    }

This allows me to remove the null checks in the constructor as the NonNullableString conversion handles that. However, if there is null value I can’t know which property is null because the exception does not state that. Unhandled exception. System.ArgumentNullException: value (Parameter ' cannot be null!')

Is there a way I can get the name of the property which uses the NonNullableString, in this example Username, so I can get more information when I handle the exception/ in logging?

EDIT: Nullable types are enabled. However, you can still give null value to string as nullable types only gives compiler warning. Above code works perfectly. Visual studio even says that test.userName is not null even though I just gave it null in one line above.

    public static void Main()
    {   
        var test = new User(null);
        
        Console.WriteLine(test.Username);
    }
    
    public class User 
    {
        public string Username { get; set; }
        
        public User(string username)
        {
            Username = username;    
        }
    }

2

Answers


  1. Null values aren’t always as simple as it seems. I’ve had to use DBNull.Value to represent a null instead of just the ‘null’ word itself

    Login or Signup to reply.
  2. Consider using nullable reference types instead. Use string to mean a non-nullable string, and use string? to mean a string that can be null.

    Although there aren’t runtime checks when you pass null to a non-nullable parameter like you have here, there are compiler warnings. If you make sure there are no warnings, it would be very rare to have an unexpected NullReferenceException occur. You also have the option to turn all the related warnings into errors, so that it doesn’t even compile.


    There is no way to do what you want without also changing the call site.

    One solution would be to remove the implicit conversion and force callers to use the constructor. This way, you can get the caller argument expression:

    private NonNullableString(string value, [CallerArgumentExpression("value")] string expr = "")
    {
        if (value == null)
            throw new ArgumentNullException($"{expr} cannot be null!", expr);
        Value = value;
    }
    
    // callers would need to do:
    Username = new(username);
    

    Side note: Your NonNullableString doesn’t entirely solve the "null" problem, since anyone can use the parameterless constructor of a struct to make a NonNullableString that contains a null string, i.e. NonNullableString x = new();.

    Login or Signup to reply.
Please signup or login to give your own answer.
Back To Top
Search