37

I'm responsible for a LINQ provider which performs some runtime evaluation of C# code. As an example:

int? thing = null;
accessor.Product.Where(p => p.anInt == thing.GetValueOrDefault(-1))

Currently the above code doesn't work with my LINQ provider due to thing being null.

While I've been working with C# for a long time, I don't know how GetValueOrDefault is implemented and therefore how I should resolve this.

So my question is: how does GetValueOrDefault work in the case that the instance on which it is called is null? Why isn't a NullReferenceException thrown?

A follow on question: how should I go about replicating a call to GetValueOrDefault using reflection, given that I need to handle null values.

Ian Newson
  • 7,679
  • 2
  • 47
  • 80
  • 7
    The `Nullable<>` struct is special. Being a struct means it can not really be `null`, but the language allows you to set it to `null` which just creates an instance with `HasValue` set to false. `GetValueOrDefault` is likely not working here because you are using EF (or some other query provider) that doesn't know how to translate it to SQL. – juharr Apr 14 '15 at 11:44
  • 3
    what do you mean by "it does not work"? – default Apr 14 '15 at 11:44
  • "doesn't work" - what exactly happens? – O. R. Mapper Apr 14 '15 at 11:44
  • @RudiVisser Sorry, I think you've misunderstood the question. I know how to resolve this specific instance, but I'm not sure what in the CLR allows instance methods to be called on null types. – Ian Newson Apr 14 '15 at 11:44
  • Maybe you should show the code for your Linq provider that isn't working for this case. – juharr Apr 14 '15 at 11:49

4 Answers4

55

thing isn't null. Since structs can't be null, so Nullable<int> can't be null.

The thing is... it is just compiler magic. You think it is null. In fact, the HasValue is just set to false.

If you call GetValueOrDefault it checks if HasValue is true or false:

public T GetValueOrDefault(T defaultValue)
{
    return HasValue ? value : defaultValue;
}
Edward Brey
  • 40,302
  • 20
  • 199
  • 253
Patrick Hofman
  • 153,850
  • 22
  • 249
  • 325
  • But when using reflection to retrieve the value of an instance of a null Nullable, null is actually returned. – Ian Newson Apr 14 '15 at 11:46
  • It is still magic in the compiler / debugger, etc. The internals obfuscate what is actually happening. See the link to the reference source in my answer. – Patrick Hofman Apr 14 '15 at 11:46
  • Ok, so when it comes to reflection I just need to replicate the CLR by testing if the type is a Nullable<>, and creating an instance with HasValue set to false manually. Thanks! – Ian Newson Apr 14 '15 at 11:48
  • @IanNewson: Indeed. Need more help on getting this implemented? – Patrick Hofman Apr 14 '15 at 15:25
  • No, thanks for the offer. In the end I had to hard code an implementation of GetValueOrDefault into my expression evaluator. Far less than ideal, but I'd be shocked if the .NET logic changed. – Ian Newson Apr 14 '15 at 15:32
12

GetValueOrDefault () prevents errors that may occur because of null. Returns 0 if the incoming data is null.

int ageValue = age.GetValueOrDefault(); // if age==null

The value of ageValue will be zero.

Murat Kara
  • 791
  • 4
  • 15
3

A NullReferenceException isn't thrown, because there is no reference. The GetValueOrDefault is a method in the Nullable<T> structure, so what you use it on is a value type, not a reference type.

The GetValueOrDefault(T) method is simply implemented like this:

public T GetValueOrDefault(T defaultValue) {
    return HasValue ? value : defaultValue;
}

So, to replicate the behaviour you just have to check the HasValue property to see what value to use.

Guffa
  • 687,336
  • 108
  • 737
  • 1,005
  • I can't check `HasValue` when using reflection, because the instance returned is actually null. – Ian Newson Apr 14 '15 at 11:49
  • @IanNewson: It sounds like getting the instance means that you get the value of the variable. That would get the value and convert it to an object. You should look at the variable itself instead of getting its value. – Guffa Apr 14 '15 at 11:53
-2

I think you provider was not working correctly. I've made a simple test and it worked correctly.

using System;
using System.Linq;

namespace ConsoleApp4
{
    class Program
    {
        static void Main(string[] args)
        {
            var products = new Product[] {
                new Product(){ Name = "Product 1", Quantity = 1 },
                new Product(){ Name = "Product 2", Quantity = 2 },
                new Product(){ Name = "Product -1", Quantity = -1 },
                new Product(){ Name = "Product 3", Quantity = 3 },
                new Product(){ Name = "Product 4", Quantity = 4 }
            };

            int? myInt = null;

            foreach (var prod in products.Where(p => p.Quantity == myInt.GetValueOrDefault(-1)))
            {
                Console.WriteLine($"{prod.Name} - {prod.Quantity}");
            }

            Console.ReadKey();
        }
    }

    public class Product
    {
        public string Name { get; set; }
        public int Quantity { get; set; }
    }
}

It produces as output: Product -1 - -1

  • Sorry I think you’ve misunderstood, I’m the author of the link provider in this case. I know it works fine with Linq to objects as in your example. – Ian Newson Nov 15 '17 at 22:13
  • That's why I've said "I think you provider was not working correctly.". Your question was about the funcionality of the "GetValueOrDefault in the case that the instance on which it is called is null". – Charles Schneider Nov 15 '17 at 22:16
  • @CharlesSchneider Pretty much all query providers, *by design*, don't support every possible operation that you could do in LINQ to objects. Some are intentionally not supported, usually due to the nature of the query provider and what it's actually querying making a translation of the given expression not possible for the data source it represents. – Servy Nov 15 '17 at 22:20