Is there a way to have a C# class handle its own null reference exceptions

前端 未结 7 1148
青春惊慌失措
青春惊慌失措 2021-02-05 05:05

Question

I\'d like to have a class that is able to handle a null reference of itself. How can I do this? Extension methods are the only way I can think

7条回答
  •  余生分开走
    2021-02-05 05:18

    The only way this can work is if you use an extension method or other static method to handle the null reference.

    NullReferenceExceptions (NullPointerExceptions for the Javaheads; roughly synonymous) occur when code is told to call a method belonging to an object instance that doesn't actually exist. The thing you must keep in mind is that null is not actually any object. A variable of a type can be set to null, but that simply means the variable doesn't reference an instance.

    Therein lies the problem; if a variable, regardless of its type (as long as that's a nullable type), is null, then there isn't an instance on which to call the method, and an instance is required for instance methods because that's how the program determines the state of members accessible to the method. If MyClass had a MyField and MyMethod(), and you called MyMethod on a null reference of MyClass, what's the value of MyField?

    The solution is usually to move to static scope. Static members (and classes) are guaranteed to have state because they are instantiated once at runtime (usually just-in-time, as in before first reference). Because they always have state, they can always be called and so can be given things to do that may not be able to be done at the instance level. Here's a method you can use in C# to return a value from an object member chain which may otherwise result in an NRE:

    public static TOut ValueOrDefault(this TIn input, Func projection, 
           TOut defaultValue = default(TOut))
        {
            try
            {
                var result = projection(input);
                if (result == null) result = defaultValue;
                return result;
            }
            catch (NullReferenceException) //most nulls result in one of these.
            {
                return defaultValue;
            }
            catch (InvalidOperationException) //Nullables with no value throw these
            {
                return defaultValue;
            }
        }
    

    Usage:

    class MyClass {public MyClass2 MyField;}
    class MyClass2 {public List MyItems; public int? MyNullableField;}
    
    ...
    var myClass = null;
    //returns 0; myClass is null
    var result = myClass.ValueOrDefault(x=>x.MyField.MyItems.Count);
    myClass = new MyClass();
    //returns 0; MyField is null
    result = myClass.ValueOrDefault(x=>x.MyField.MyItems.Count);
    myClass.MyField = new MyClass2();
    //returns 0; MyItems is null
    result = myClass.ValueOrDefault(x=>x.MyField.MyItems.Count);
    myClass.MyField.MyItems = new List();
    //returns 0, but now that's the actual result of the Count property; 
    //the entire chain is valid
    result = myClass.ValueOrDefault(x=>x.MyField.MyItems.Count);
    //returns null, because FirstOrDefault() returns null
    var myString = myClass.ValueOrDefault(x=>x.MyField.MyItems.FirstOrDefault());
    myClass.MyField.MyItems.Add("A string");
    //returns "A string"
    myString = myClass.ValueOrDefault(x=>x.MyField.MyItems.FirstOrDefault());
    //returns 0, because MyNullableField is null; the exception caught here is not an NRE,
    //but an InvalidOperationException
    var myValue = myClass.ValueOrDefault(x=>x.MyField.MyNullableField.Value);
    

    While this method has value in situations which would otherwise call for long nested ternary operators to produce something (anything) to show the user or use in a calculation, I do NOT recommend using this pattern to perform actions (void methods). Because no NREs or IOEs will be thrown out, you'll never know if what you asked it to do was actually done. You may be able to get away with a "TryPerformAction()" method which returns true or false, and/or has an output parameter containing the thrown exception (if any). But if you're going to go to that kind of trouble, why not just try/catch the thing yourself?

提交回复
热议问题