roslyn: Please add new syntax keyword "ref?" to represent that "ref return" result might be null

Now the C#7.0 feature “ref return” doesn’t allow null as return value, but null as return value is sometimes useful. For example:

static ref? int findvalue(int[] arr, int value){
    int i;
    for(i=0;i<arr.Length;i++){
        if(value==arr[i])return ref arr[i];
    }
    return null; //not found
}

int[] array=new int[]{1,2,3};
ref? int e=findvalue(array, 4);
if(e!=null)e=5;
ref? int e1=finevalue(array,3);
if(e1!=null)e1=0;
..........

About this issue

  • Original URL
  • State: closed
  • Created 7 years ago
  • Comments: 15 (8 by maintainers)

Most upvoted comments

I am wondering why the given scenario is a problem with ref returns and was not a problem with ref parameters…

With ref parameters, if I don’t care about some parameter, using a dummy local variable with a default value is probably enough and then I can just ignore it after the call.

With ref returns, if I don’t have any variable to return, the caller probably has to be be told about it somehow (possibly using a bool flag, as you suggested) and I have to somehow allocate storage for the dummy variable on the heap (or it could be a static field).

So I think it indeed is a bigger issue with ref returns than with ref parameters.

@jnm2

What’s the workaround?

Returning ref int? is probably as close as you get, short of digging into unsafe territory at which point you’d probably be better off skipping ref and diving straight into pointers.

From the blog post above it sounds very much like this request to be able to work with ref as if it’s a “pointer-lite” was never intended. That ref as a modifier doesn’t change what the type is, only where it is. But given the milestone tag maybe they’re willing to reconsider it. My question would be what additional work might need to be done with the verifier to enable such scenarios, as I understand that ref returns already ran afoul of some of the code safety enforcement features.

Here is a type representing either null or a ref.

using System;
using System.Runtime.CompilerServices;

unsafe struct NullableRef<T>
{
    void* _ref;
    public NullableRef(ref T r) => _ref = Unsafe.AsPointer(ref r);
    public bool IsNull => _ref == (void*)0;
    public ref T Reference => ref Unsafe.AsRef<T>(_ref);
}

class Program
{
    static void Main()
    {
        var array = new[] { 1, 2, 3, 4, 5 };

        var x = Find(array, 3);
        if (!x.IsNull) x.Reference = 0;

        var y = Find(array, 6);
        if (!y.IsNull) y.Reference = 0;

        Console.WriteLine(string.Join(", ", array));
    }

    static NullableRef<int> Find(int[] array, int value)
    {
        for (int i = 0; i < array.Length; i++)
        {
            if (array[i] == value) return new NullableRef<int>(ref array[i]);
        }
        return default(NullableRef<int>);
    }
}

@ygc369

You can return null as ref int by using the Unsafe class

using System;
using System.Runtime.CompilerServices;

class Program
{
    static void Main()
    {
        int[] array = new int[] { 1, 2, 3 };

        {
            ref int e = ref FindValue(array, 4);
            if (!IsNull(ref e)) e = 5;
        }

        {
            ref int e = ref FindValue(array, 3);
            if (!IsNull(ref e)) e = 0;
        }

        foreach (int x in array)
        {
            Console.WriteLine(x); // 1, 2, 0
        }
    }

    static ref int FindValue(int[] arr, int value)
    {
        int i;
        for (i = 0; i < arr.Length; i++)
        {
            if (value == arr[i]) return ref arr[i];
        }
        return ref NullRef<int>();
    }

    unsafe static ref T NullRef<T>() where T : struct => ref Unsafe.AsRef<T>((void*)0);
    unsafe static bool IsNull<T>(ref T r) where T : struct => Unsafe.AsPointer(ref r) == (void*)0;
}

@ygc369

Given that the answer was, “Not possible. Not, unless something is fundamentally broken,” why do you think there would need to be a way to test for null? A ref return can’t be null, just like ref parameters can’t be null, and there’s never been a way to test them for some state that they cannot/shouldn’t be.