How does GetValueOrDefault work?
Asked Answered
P

5

40

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.

Phototonus answered 14/4, 2015 at 11:38 Comment(5)
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.Europa
what do you mean by "it does not work"?Human
"doesn't work" - what exactly happens?Eyetooth
@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.Phototonus
Maybe you should show the code for your Linq provider that isn't working for this case.Europa
A
59

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;
}
Activism answered 14/4, 2015 at 11:45 Comment(5)
But when using reflection to retrieve the value of an instance of a null Nullable, null is actually returned.Phototonus
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.Activism
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!Phototonus
@IanNewson: Indeed. Need more help on getting this implemented?Activism
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.Phototonus
D
14

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.

Dremadremann answered 15/1, 2019 at 7:32 Comment(0)
N
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.

Nawrocki answered 14/4, 2015 at 11:47 Comment(2)
I can't check HasValue when using reflection, because the instance returned is actually null.Phototonus
@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.Nawrocki
U
-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

Underground answered 15/11, 2017 at 22:11 Comment(3)
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.Phototonus
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".Underground
@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.Municipalize
U
-2

Hi GetValueOrDefault() has been in c# for some time now.

Guid? nullableGuid = null;
Guid guid = nullableGuid.GetValueOrDefault()
Unhair answered 14/11, 2023 at 8:9 Comment(1)
How does this answer the OP's question?Spark

© 2022 - 2024 — McMap. All rights reserved.