1

When C# compares a nullable type to a "null", does the C# compiler insert some code to check if the variable has a value first?

int? fk2=null;
OtherNullable<int> fk3=null;

Console.WriteLine(fk2 == null);

Console.WriteLine(fk3 == null);

Context, I'm trying to work out what the equality + implicit, and explicit cast should look like to enable the comparison of fk3 and null.

Say that you implemented Nullable as per the code posted here: How does a Nullable<T> type work behind the scenes? and called it OtherNullable<T>

This works fine with little modification, except that val == null will not work at runtime.

Implementation:

[Serializable, StructLayout(LayoutKind.Sequential)]
public struct OtherNullable<T> : IEquatable<OtherNullable<T>> where T : struct {

    private bool hasValue;
    internal T value;

    public OtherNullable(T value)
        : this() {
        this.value = value;
        this.hasValue = true;
    }

    public OtherNullable(T? value) {
        this.value = default(T);
        this.hasValue = false;
    }

    public bool HasValue {
        get { return this.hasValue; }
    }

    public T Value {
        get {
            if (!this.HasValue) {
                throw new InvalidOperationException("No Value");
            }
            return this.value;
        }
    }

    public OtherNullable(bool b) {
        this.value = default(T);
        this.hasValue = false;
    }

    public T GetValueOrDefault() {
        return this.value;
    }

    public T GetValueOrDefault(T defaultValue) {
        if (!this.HasValue) {
            return defaultValue;
        }
        return this.value;
    }

    public bool Equals(OtherNullable<T> other)
    {
        if (!other.HasValue & !this.HasValue)
            return true;
        else
            return other.hasValue.Equals(hasValue) && other.value.Equals(value);
    }

    public static bool operator ==(OtherNullable<T> left, OtherNullable<T> right) {
        return left.Equals(right);
    }

    public static bool operator !=(OtherNullable<T> left, OtherNullable<T> right) {
        return !left.Equals(right);
    }

    public override bool Equals(object other) {
        if (ReferenceEquals(null, other)) return false;
        if (other.GetType() != typeof(OtherNullable<T>)) return false;
        return Equals((OtherNullable<T>)other);
    }

    public override int GetHashCode() {
        unchecked {
            return (hasValue.GetHashCode() * 397) ^ value.GetHashCode();
        }
    }

    public override string ToString() {
        if (!this.HasValue) {
            return "";
        }
        return this.value.ToString();
    }

    public static implicit operator OtherNullable<T>(T value) {
        return new OtherNullable<T>(value);
    }

    public static explicit operator T(OtherNullable<T> value) {
        return value.Value;
    }

    public static implicit operator OtherNullable<T>(Nullable<T> value) {
        if (value.HasValue) {
            return new OtherNullable<T>(value.Value);
        } else {
            return new OtherNullable<T>(null);
        }
    }

}

Test Code (will not compile -- it was previously):

[TestFixture]
public class TestOtherNullable {
    [Test]
    public void Test()
    {
        var a = new OtherNullable<int>();

        a = 1;

        Assert.IsTrue(a.HasValue);
        Assert.IsFalse(a == null);

        var b = new OtherNullable<int>();
        b = null;
        Assert.IsTrue(b == null);
    }
}
Community
  • 1
  • 1
sgtz
  • 8,849
  • 9
  • 51
  • 91

2 Answers2

8

The equality check of any Nullable<T> to null is equivalent to !nullable.HasValue. There are no other semantics for it that would make sense. I 'm not sure what the deal with OtherNullable<T> is, since that's not a standard type.

Jon
  • 428,835
  • 81
  • 738
  • 806
  • I added a link to some code that was posted to SO. It's supposed to be what reflector decompiles the Nullable struct to. See new info in body of question. – sgtz Mar 19 '12 at 12:40
  • 2
    @sgtz: If the question is why `fk3 == null` does not work, the answer is because you have not [overridden the equality operator](http://msdn.microsoft.com/en-us/library/7h9bszxx.aspx). I still do not understand why you would want to implement `OtherNullable` yourself. – Jon Mar 19 '12 at 12:45
  • see response from Bob2Chiv. The compiler does insert code. I did override the equality operator. – sgtz Mar 19 '12 at 13:15
  • I stand corrected. Had another go at implementing equality operators... and... it worked (see updated question code). If C# does implement the if tests listed above, I wonder if it does this for speed? Interesting. Thanks to all who contributed. – sgtz Mar 19 '12 at 13:25
2

Q: "When C# compares a nullable type to a "null", does the C# compiler insert some code to check if the variable has a value first?"

A: The compiler does not insert code in this instance. In this case, the Equals operator is overridden like this:

public override bool Equals(object other)
{
    if (!this.HasValue)
    {
        return (other == null);
    }
    if (other == null)
    {
        return false;
    }
    return this.value.Equals(other);
}

Which does take care of comparing it to null.

The compiler does help you in the following ways:

// this
int? x = null;
// Transformed to this
int? x = new Nullable<int>()

// this
if (x == null) return;
// Transformed to this
if (!x.HasValue) return;

// this
if (x == 2) return;
// Transformed to this
if (x.GetValueOrDefault() == 2 && x.HasValue) return;

All of this information is in the question that you linked.

Thanks to @barrylloyd and @ChaosPandion

StayOnTarget
  • 11,743
  • 10
  • 52
  • 81
Bob2Chiv
  • 1,858
  • 2
  • 19
  • 29
  • +1: I must have missed **if (!x.HasValue) return;** and **if (x.GetValueOrDefault() == 2 && x.HasValue) return;**. I thought that you might have been able to be smart with the cast and equality overloads. I'll leave this question open a little bit longer though just in case someone knows a way. ty – sgtz Mar 19 '12 at 13:04