1

Behold the following example with System.Nullable<T> in C#.

int x = 5;
int? y = 3;
int? result = x + y; //no compiler warning

It makes sense that the compiler can figure out that T is an int, thus it can use the operator.
Also in

int x = 5;
int? y = 3;
bool result = x == y; //no compiler warning

it makes sense, if x was null, the expression would be false. The compiler doesn't mind.

Now I'm trying to create a lookalike Nullable<T> class. Let's call it Lookable<T>.

[Serializable]
public struct Lookable<T> where T : struct
{
    public Lookable(T value)
    {
        Value = value;
    }

    public T Value { get; }

    public override bool Equals(object other)
    {
        return other != null && Value.Equals(other);
    }

    public override int GetHashCode()
    {
        return Value.GetHashCode();
    }

    public override string ToString()
    {
        return Value.ToString();
    }

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

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

The idea here is straight from .NET's source code. In my case I'm just omitting the HasValue property. Now this example would work:

int x = 6;
Lookable<int> y = x;
Lookable<int> z = 4;

The compiler can infer the types here because of the implicit operator correct?
What I don't understand is that this example will make the compiler unhappy:

int x = 5;
Lookable<int> y = 3;
var result1 = x + y; //compile error
var result2 = x == y; //compile error

The compiler is giving me the message:

Operator cannot be applied to operands of type 'int' and 'Lookable<int>'.

Why not? And why is it possible with Nullable<T>? I can't find it anywhere in the source code. Would it also be possible for Lookable<T>?

QuantumHive
  • 5,613
  • 4
  • 33
  • 55
  • Ok, i read wrong the example, y + 5 will work, x + y will fail... – Gusman May 05 '17 at 14:15
  • What the heck, it works with both... – Gusman May 05 '17 at 14:16
  • Check this gist, that's my full optional class, it mimics 100% nullable and does not cause a compiler exception: https://gist.github.com/gusmanb/fc6662d64195d4230164a7700fdaa135 – Gusman May 05 '17 at 14:18

1 Answers1

5

The code for this isn't in Nullable<T> - it is in the C# compiler, in particular "lifted operators" in the specification, and how they apply specifically to System.Nullable<T>. The specification references are in this answer.

You cannot reproduce the Nullable<T> behaviour in your own types. It has special handling by both the compiler and the runtime (boxing, etc).

Community
  • 1
  • 1
Marc Gravell
  • 1,026,079
  • 266
  • 2,566
  • 2,900
  • Mark, can you explain me why this struct works like nullable? https://gist.github.com/gusmanb/fc6662d64195d4230164a7700fdaa135 I created it myself and does not cause an exception when doing something like `Optional c = 33; int fd = 33; int f = c + fd;`... now I read about lifted operators and it should cause the exception, but it compiles fine... I'm really intrigued... Am I missing something? – Gusman May 05 '17 at 14:21
  • 1
    @Gusman because there is an implicit conversion from `Optional` to `T` (`public static implicit operator T(Optional value)`) that can be used to find the `+` for `int`, so basically this is interpreted as `int f = (int)c + fd;`. Implicit conversion operators are explored for operators. – Marc Gravell May 05 '17 at 14:35
  • Ok... missed the part on the user code where he defined it as explicit. So to make the user code work he only needs to add the implicit operator, right? Thanks for the aclaration anyway. – Gusman May 05 '17 at 14:37
  • @Gusman well... sort of; it doesn't result in the same outcomes as lifted operators do, note. For lifted operators, `null + 5 => null`; your code would result in `5`. – Marc Gravell May 05 '17 at 14:38
  • Totally right Marc, that's the difference. Really, thanks a lot for the info. I use this struct every day and I was scared if it could crash because something I missed. – Gusman May 05 '17 at 14:39