3

I ran into a null reference exception on line:

dr["IsSandpit"] = p.MineralName.Equals("Sand");

Of course the fix is:

dr["IsSandpit"] = p.MineralName!=null && p.MineralName.Equals("Sand");

But I am wondering if a nullable type can be configured that the Equals method instead of throwing a null reference exception would just return false for a strongly typed variable? Or some method NotNullAndEquals?

Null in the database means something like "not filled" or empty and "empty" surely is not equal to "Sand". (Perhaps this is a duplicate but I have not found the right question)

Vojtěch Dohnal
  • 7,867
  • 3
  • 43
  • 105
  • 8
    `"Sand".Equals(p.MineralName)`? – Rawling Feb 19 '18 at 09:37
  • 4
    `p.MineralName == "Sand"` – mjwills Feb 19 '18 at 09:37
  • 3
    Your interpretation of the issue is slightly off, which is changing your expectations. The exception is not thrown _inside_ the `Equals()` function, it's merely the consequence of trying to access _anything_ from a `null` value. Whether you were trying to access a property, method, or field is irrelevant, the NRE will be thrown regardless. If you want to implement a workaround here, it should be implemented for the **value** of `p.MineralName`, not for the **algorithm** of `.Equals()`. You're "blaming" the wrong party for the exception. – Flater Feb 19 '18 at 09:38
  • @mjwills But I sometimes put arguments to Equals like culture. – Vojtěch Dohnal Feb 19 '18 at 09:38
  • In that case, use @Rawling's solution. For a straight up equals, use `==`. – mjwills Feb 19 '18 at 09:39
  • 1
    string.Equals(p.MineralName, "Sand") – Johnny Feb 19 '18 at 09:44
  • dr["IsSandpit"] = (p.MineralName != null ? p.MineralName.Equals("Sand") : false) – Lord Darth Vader Feb 19 '18 at 09:44
  • @Flater but MineralName is of type `string`...? And Equals is a static method. – Vojtěch Dohnal Feb 19 '18 at 09:44
  • 2
    @VojtěchDohnal: `MineralName` is of **type** string, but it has the **value** of `null`. Also, the `Equals()` you used is **not static**. `String.Equals(foo,bar)` is static. `foo.Equals(bar)` is not. Statics are of the form `ClassName.MethodName()`, not `variableName.MethodName()`. – Flater Feb 19 '18 at 09:46
  • Ok, I got it... – Vojtěch Dohnal Feb 19 '18 at 09:47
  • 1
    Possible duplicate of [Using the Null Conditional Operator to check values on objects which might be null](https://stackoverflow.com/questions/45796633/using-the-null-conditional-operator-to-check-values-on-objects-which-might-be-nu) – John Wu Feb 19 '18 at 09:51
  • @Flater Where did you learn that? `Implementations of Equals must not throw exceptions; they should always return a value. For example, if obj is null, the Equals method should return false instead of throwing an ArgumentNullException.` quoted from "https://learn.microsoft.com/en-us/dotnet/api/system.object.equals?view=netframework-4.8#System_Object_Equals_System_Object_" Wasted some time to discover where the bug was coming from – mireazma Sep 08 '19 at 11:35
  • To serve the community instead of a single particular case, consider the case of comparing two generic types: `==` can't be used as C# doesn't support the operator for generic types; `Equals` throws exception instead of returning bool (contrary to .NET documentation) so we're forced to _always_ precheck for null. – mireazma Sep 08 '19 at 11:47
  • @mireazma: The documentation cannot possibly refer to `myValue` in `myValue.Equals(myOtherValue)` notation, since a `null` in `myValue` will **always** lead to a null reference exception regardless of the content of the `Equals()` method. As mentioned in your documentation, null handling is expected of the **parameter** of the function, i.e. `obj` in your documentation and `myOtherValue` in my above example. – Flater Sep 09 '19 at 16:50
  • @mireazma Additionally, [the documentation you linked](https://learn.microsoft.com/en-us/dotnet/api/system.object.equals?view=netframework-4.8#System_Object_Equals_System_Object_) mentions the following: "The static `Equals(Object, Object)` method indicates whether two objects, `objA` and `objB`, are equal. It also enables you to test objects whose value is null for equality." Notice that the **static** `Equals(objA, objB)` is a **different method** than the `objA.Equals(objB)` method we are discussing here. – Flater Sep 09 '19 at 16:53
  • @Flater I apologize - you are right, I misread the documentation. And I saw the static method but I wasn't referring to it. I've just read that Eric Lippert mentioned `null` having a type, albeit inaccessible. – mireazma Sep 09 '19 at 19:23

3 Answers3

5

You can call the static Equals method on both operands:

var isEqual =  string.Equals("Sand", p.MineralName); // return false for null.

This will remove the need to access a potentially null reference. Alternately, you can use the null propagation operator to avoid the operation if the value is null:

var isEqual = p.MineralName?.Equals("Sand"); // return null for null.

These are much better approaches, IMO, than using extension methods to hack together a method that can theoretically be called on a null reference, since that approach leads to confusion on whether you can safely call a method without checking for null. Imagine this code:

 if (!p.MineralName.Equals("Sand"))
 {  
       return p.MineralName.ToUpper(); // throws NullReferencException!
 }
Avner Shahar-Kashtan
  • 14,492
  • 3
  • 37
  • 63
  • Does the null propagation approach not mean that the resulting value is `null`, rather than `false`? This would implicitly make `var isEqual` of type `int?` rather than `int`. – Flater Feb 19 '18 at 09:49
  • 1
    " return false for null." - not true, it returns null for null – Evk Feb 19 '18 at 09:50
0

The expression

p.MineralName?.Equals("Sand")

will evaluate to null if p.MineralName is null. You can then coalesce it to false, like so:

p.MineralName?.Equals("Sand") ?? false
John Wu
  • 50,556
  • 8
  • 44
  • 80
-1

As an option you can utilize a self-created NotNullAndEquals extension method:

    public static class Utilities {
        public static bool NotNullAndEquals(this string value, string expected) {
            return value != null && value.Equals(expected);
        }
    }
serge.karalenka
  • 980
  • 1
  • 15
  • 29