Question

ok so i want to make a generic class that will change the value of a datatype. The reason i want to do this is so i can have undo and redo methods. I could write a class for each valuetype i need. I.E. double, int... but it would be much easier if i could create a generic class to do this.

This is what i have

class CommandChangeDouble : Command
{
    double _previous;
    double _new;
    double* _objectRef;

    public unsafe CommandChangeDouble(double* o, double to)
    {
        _objectRef = o;
        _previous = *o;
        _new = to;
        *_objectRef = _new;
    }

    public unsafe void Undo()
    {
        *_objectRef = _previous;
    }
    public unsafe void Redo()
    {
        *_objectRef = _new;
    }
}

this is what i want

class CommandChangeValue<T> : Command
{
    T _previous;
    T _new;
    T* _objectRef;

    public unsafe CommandChangeValue(T* o, T to)
    {
        _objectRef = o;
        _previous = *o;
        _new = to;
        *_objectRef = _new;
    }

    public unsafe void Undo()
    {
        *_objectRef = _previous;
    }
    public unsafe void Redo()
    {
        *_objectRef = _new;
    }
}

but this gives me the error Error "Cannot take the address of, get the size of, or declare a pointer to a managed type ('T')"

Is there a better way to do this or a way to get around this error?

Was it helpful?

Solution 3

Instead of supplying a pointer to the value, supply a setter:

class CommandChangeValue<T> : Command
{
    T _previous;
    T _new;
    Action<T> _set;

    public CommandChangeValue(T value, Action<T> setValue, T newValue)
    {
        _previous = value;
        _new = newValue;
        _set = setValue;
        setValue(_new);
    }

    public void Undo() { _set(_previous); }
    public void Redo() { _set(_new); }
}


// ...
double v = 42;
var c = new CommandChangeValue(v, d => v = d, 99);

OTHER TIPS

Just for the record you can get a pointer to a generic type or any other type using these methods....

    /// <summary>
    /// Provides the current address of the given element
    /// </summary>
    /// <typeparam name="T"></typeparam>
    /// <param name="t"></param>
    /// <returns></returns>
    [System.Runtime.CompilerServices.MethodImpl(System.Runtime.CompilerServices.MethodImplOptions.AggressiveInlining)]
    public static System.IntPtr AddressOf<T>(T t)
        //refember ReferenceTypes are references to the CLRHeader
        //where TOriginal : struct
    {
        System.TypedReference reference = __makeref(t);

        return *(System.IntPtr*)(&reference);
    }

    [System.Runtime.CompilerServices.MethodImpl(System.Runtime.CompilerServices.MethodImplOptions.AggressiveInlining)]
    static System.IntPtr AddressOfRef<T>(ref T t)
    //refember ReferenceTypes are references to the CLRHeader
    //where TOriginal : struct
    {
        System.TypedReference reference = __makeref(t);

        System.TypedReference* pRef = &reference;

        return (System.IntPtr)pRef; //(&pRef)
    }

I have used them along with a few others to implement a form of slicing used with Arrays.

C# 7.3 solved that issue with new generic constraint - unmanaged.

Basically it allows to do something like that:

void Hash<T>(T value) where T : unmanaged
{
    // Okay
    fixed (T* p = &value) 
    { 
        ...
    }
}

Docs

Don't use pointers, as said in another answer and in the comments. And if you want to have some undo/redo functionality in your app, you might want to look into the Memento pattern.

Don't use pointers.

If you use normal references, and always interact with the value through a property in this class, everything will work fine.

Licensed under: CC-BY-SA with attribution
Not affiliated with StackOverflow
scroll top