Search code examples
c#objecttagsexplicitstructlayout

Is it legit code to tag an object like this?


I've once written a piece of code to add a name to a Task. The code below seems to do the same, but with less code. But I wonder, is it legit. Is it production code ready. What about garbage collection? What about the instance of the class being moved around in code (because it is not pinned), will it still work when it is moved around? How can I put this code to the test?

using System.Runtime.InteropServices;

namespace ConsoleApplication1
{
    class Program
    {
        static void Main(string[] args)
        {
            var obj = new object();
            obj.Tag("Link some data");
            var tag = obj.Tag();
        }
    }

    public static class ObjectExtensions
    {
        private class Tagger
        {
            public string Tag { get; set; }
        }

        [StructLayout(LayoutKind.Explicit)]
        private struct Overlay
        {
            [FieldOffset(0)]
            public Tagger Tagger;
            [FieldOffset(0)]
            public object Instance;
        }

        public static string Tag(this object obj)
        {
            var overlay = new Overlay {Instance = obj };
            return overlay.Tagger.Tag;
        }

        public static void Tag(this object obj, string tag)
        {
            var overlay = new Overlay {Instance = obj };
            overlay.Tagger.Tag = tag;
        }
    }
}

Solution

  • No, this is not legitimate at all. Frankly, I'm surprised that .NET and C# allow this without the /unsafe switch. Your proposal has obvious risks, but I have to admit that in all these years of coding in C#, it never occurred to me one would be able to violate safe memory access like this in C#, without explicitly enabling unsafe code.

    Consider this variation on your example:

    class A
    {
        public string Text { get; set; }
    }
    
    class Program
    {
        static void Main(string[] args)
        {
            A a = new A { Text = "Text" };
    
            a.Tag("object A tag");
    
            string tag = a.Tag(), text = a.Text;
        }
    }
    

    You will find that, on the last statement, the text variable has been set to "object A Tag". In other words, your "overlay" approach has allowed your code to reinterpret the reference to an object of class A, as a reference to an object of class Overlay, without any sort of compiler warning or run-time error at all.

    In the above example, the consequence is about as benign as you could hope for: the original property value of Text has been changed from its correct value to the text passed as the "tag". This is bad enough, but in other contexts you could find your class has been corrupted in horrible ways, leading to further data corruption or (if you're lucky) the immediate termination of your program resulting from some kind of access violation or other exception.

    Don't do this. It's very dangerous, and certainly when used in the way you proposed here, will never work correctly. You'll always be overwriting some data that you shouldn't have.