10

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?

Daniel Johnson
  • 691
  • 4
  • 14
  • 7
    The answer to this kind of question is always: Don't use pointers. – SLaks Jun 17 '13 at 20:24
  • Note that you will need a stack if you want to undo more than once. – SLaks Jun 17 '13 at 20:25
  • References in .NET are type safe pointers... Using "pure" pointers in C# is a bit of an overkill. – Geeky Guy Jun 17 '13 at 20:26
  • @SLaks Yeah i didn't want to use pointers but i couldn't think of a different way to implement what i wanted. Also yeah i have 2 List to keep track of Undo and Redo – Daniel Johnson Jun 17 '13 at 21:03
  • The problem is that you can only make a pointer of value types that don't contain references, and you cannot constrain the generic parameter to these types. – IS4 Jun 05 '15 at 21:22

5 Answers5

17

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.

Jay
  • 3,276
  • 1
  • 28
  • 38
  • How do you do the reverse and go back to type T from an IntPtr? – Mike Marynowski Mar 03 '17 at 11:16
  • 3
    Carefully, http://net7mma.codeplex.com/SourceControl/latest#Concepts/Classes/Unsafe.cs see Read or Read – Jay Mar 03 '17 at 11:57
  • 1
    Wow, awesome. How have I not seen this before, gah...would have made my life way easier trying to grab the raw bits out of enum types. I ended up using generic type-cached compiled cast expressions with too much repetitive code needed for each underlying enum type. This is lovey. I wonder which one is more performant. – Mike Marynowski Mar 03 '17 at 15:40
  • 2
    You should include the Read functions in your answer :) – Mike Marynowski Mar 03 '17 at 15:46
  • 1
    Was struggling over this last night. Needed a pointer to the first item of a generic array where it's known to me (but not the compiler) that the array is a value-type of fixed size, and to be able to pin the memory occupied by the array. Something tells me this should do the job. Also intrigued by the use of `__makeref`. Haven't seen that before. – tobriand Oct 25 '17 at 10:46
  • Remember that these IntPtr addresses could change in the middle of execution because of GC cleanup. That is why 'fixed' and memory pinning got invented and should be used with these. – Arek Bal Sep 26 '18 at 10:54
  • Also keep in mind values passed with ref are implemented as interior pointers and the address cannot change while the value is on the stack, just don't store the ref into a field. – Jay Sep 26 '18 at 16:55
  • @Jay, do you know how to do this for a generic method? I have a [question](https://stackoverflow.com/q/59778004/1938988) that was bountied and I will award a bounty again if you know the answer – johnny 5 Feb 14 '20 at 15:23
  • Just curious, author doesn't know how to `using System;`? The code is unreadable. It's also obsolete answer. – aepot Nov 03 '21 at 10:53
16

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

Voodu
  • 770
  • 7
  • 18
11

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);
Brandon
  • 38,310
  • 8
  • 82
  • 87
  • Hmm this looks like what I'm looking for with the added bonus of not using pointers. although I'm going to have to familiarize myself with the Action class. – Daniel Johnson Jun 17 '13 at 20:51
-7

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.

Geeky Guy
  • 9,229
  • 4
  • 42
  • 62
-8

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.

Community
  • 1
  • 1
SLaks
  • 868,454
  • 176
  • 1,908
  • 1,964
  • well the problem is there is going to be multiple of these classes interacting with the same value. I also can't make it static because there's going to be different values also. – Daniel Johnson Jun 17 '13 at 20:50
  • 7
    You can use pointers. Pointers are valid and sometimes important for high performance applications. This is a silly response. –  Sep 03 '16 at 16:06
  • @Ben: If you have to ask the question, you probably aren't skilled enough to do that right. – SLaks Sep 04 '16 at 13:59
  • 9
    ...you become "skilled enough" by asking questions. Again. Silly response. –  Sep 17 '16 at 01:31
  • @Ben: No; to be skilled enough to effectively use pointers, you will have to read whole books (or blog posts by Nick Craver & others). Such questions are beyond the scope of SO. – SLaks Sep 19 '16 at 03:17
  • 3
    "No; to be skilled enough to effectively use pointers, you will have to read whole books" No way. That's a ridiculous thing to say. What "whole books" are dedicated to the use of pointers? People can/should ask about using them for high performance C# applications. I use them all the time. If you don't want to respond to people's legitimate questions about them, fine, but don't mislead and scare people needlessly on the topic. And the use of pointers is a topic beyond the scope of SO? What the heck man?! –  Oct 08 '16 at 05:00
  • @Ben: To start with, CLR via C#. There are one or two others that I don't remember offhand. – SLaks Oct 09 '16 at 00:43
  • I mostly see C# noobs use pointers when they come over from C. It's a code smell. The right answer is more like, "Don't use pointers unless you really know what you're doing." – Andrew Rondeau Apr 03 '18 at 18:25
  • Most people who say these things are people who dont know how to use pointers and project their inability to do so on to others. – Matt Jun 15 '18 at 06:42
  • 1
    I use pointers whenever necessary for performance critical code. Never read a book about it either. Not hard at all once you learn them, as with anything else, much easier when coming from C/C++ background where they are most widely used, but I will never understand the "I am awesome because I know pointers, those who don't are too stupid to explain them to" mentality. I have yet to meet someone who was born with "All-knowing Pointer" gene, and didn't start off not having a clue about them. – ForeverZer0 Aug 20 '18 at 04:21