Search code examples
c#.netgenericsref

Return reference of generic


How can I achieve something like this?

public class BlaBlaBla
{
    private PotatoesContainer _containerField;
    private bool _booleanField;

    private ref T GetBackingFieldRef<T>(string propertyName)
    {
        if (string.IsNullOrWhiteSpace(propertyName))
            throw new ArgumentNullException(nameof(propertyName));

        if (propertyName == "BooleanProperty")
            return ref _booleanField;
        else if (propertyName == "ContainerProperty")
            return ref _containerField;

        throw new ArgumentException("Property does not exist", nameof(propertyName));
    }

    public void ManageProperty<T>(string propertyName, T newValue)
    {

        //Check stuff;

        var referenceToField = GetBackingFieldRef<T>(propertyName);

        referenceToField = newValue;

    }
}

I searched in all ref documentation and I cannot see anything. And I cannot cast calling (T).

Is this possible?

Is there some other way (with Action, Func or something)?


Solution

  • It's doable (as are most things), if you resort to using unsafe code. I would agree with the commenters that this is a bad idea and there are plenty of better ways to achieve what you're trying to do. However for educational purposes, here goes anyway:

    using System.Runtime.CompilerServices;
    
    ...
    
    public unsafe ref T GetBackingFieldRef<T>(string propertyName)
    {
        if (string.IsNullOrWhiteSpace(propertyName))
            throw new ArgumentNullException(nameof(propertyName));
    
        switch (propertyName)
        {
            case "BooleanProperty" when typeof(T) == typeof(bool):
                return ref Unsafe.AsRef<T>(Unsafe.AsPointer(ref _booleanField));
            case "ContainerProperty" when typeof(T) == typeof(PotatoesContainer):
                return ref Unsafe.AsRef<T>(Unsafe.AsPointer(ref _containerField));
            default:
                throw new ArgumentException("Property does not exist or type mismatch", nameof(propertyName));
        }
    }
    

    This uses Unsafe.AsPointer<T>() and Unsafe.AsRef<T>() to allow the otherwise difficult conversion to ref T. There's some checking of types to ensure that the type of T matches the type of the backing field, but this is of course very fragile - if you change the type of a backing field you'll have to remember to change the type checks, and will have to manually find all the calls to GetBackingFieldRef<T>() for that field and update them otherwise you'll get exceptions and/or memory corruption at runtime.