Mikhail
Mikhail

Reputation: 1303

How to check if generic function is called with nullable generic argument

I'm trying to write a generic function that behaves differently depending on whether it was called with an argument that allows nulls or not. In pre-C#8 world I was using something like this:

T MyFunc<T>()
{
  object value = SomeExternalFunctionCall();
  if (value == null)
  {
    if (typeof(T).AcceptsNulls())
    {
      return null;
    }
    else
    {
      throw new Exception($"{typeof(T).Name} does not support nulls");
    }
  }
  return (T)value;
}

Where AcceptsNulls() was like this:

public static bool IsAcceptNulls(this Type type)
{
  if (type.IsValueType)
  {
    if (Nullable.GetUnderlyingType(type) != null)
    {
      return true;
    }
    return false;
  }
  return true;
}

So MyFunc worked happily for both reference and value types, with proper support for nullable value types.

But now I want to have it in a nullable-reference-types-enabled code. And I'd like it to be able to distinguish MyFunc<object>() and MyFunc<object?>() cases, so it would throw when handling a null value in former case, but not in latter.

It's clear that I can't check this via type check as reference type nullability is a compile-time feature.

How could I do this? Is it possible at all?


Update

It seems that my intention is not clear so I'd go into greater details.

I have some code that produces values. Sometimes it may produce nulls. And I want to give user of my code ability to decide whether he wants to get nulls from my code or no.

With nullable structs it does work just fine:


int noNullsForThisVariable = MyFunc<int>();

int? nullsAreOkHere = MyFunc<int?>();

I want to provide same behaviour for reference types now.

Upvotes: 1

Views: 732

Answers (2)

CleanCoder
CleanCoder

Reputation: 2867

Let me explain why your approach is not possible for reference types at all. (at least until c# 8. this behavior might change in future c# versions)

Because reference types are always nullable at runtime, there is not difference in the Type itself.

this is probably why you can't do: typeof(string?) You'll get an error like: "typeof cannot be used for nullable reference types"

But that doesn't neccessarily mean that the compile doesn't add a distinguishable attribute or something similar. (which indeed could be)

But we can say, if GetType() of both type of strings will result in the same type instance, this canno be a different type at all.

Check it yourself with the following Code.

    string nonNullableString = "3434";
    string? nullableString = "3434";

    var nnStringType = nonNullableString.GetType();
    var stringType = nullableString.GetType();

    var isEqual = nnStringType == stringType; // true
    if(object.ReferenceEquals(nnStringType, stringType)) // true
        Debug.Print("it's the same object");

Nullable and non nullable string are of the same type.

Upvotes: 2

CleanCoder
CleanCoder

Reputation: 2867

There might be a missunderstanding.

The non-nullable reference types e.g. string (instead of string?) only exist at design time. At Runtime string is nullable, no matter if you just wrote string instead of string?. With that being clarified, the question might be answered. In terms of nullability, nothing has changed with c# 8.

Upvotes: 2

Related Questions