2

我曾经编写过一段代码来为 Task 添加名称。下面的代码似乎做同样的事情,但代码更少。但我想知道,这是否合法。是否准备好生产代码。垃圾回收呢?在代码中移动的类的实例怎么样(因为它没有固定),它在移动时是否仍然有效?如何将此代码进行测试?

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;
        }
    }
}
4

1 回答 1

3

不,这根本不合法。坦率地说,我很惊讶 .NET 和 C# 允许在没有/unsafe开关的情况下这样做。您的建议有明显的风险,但我不得不承认,在使用 C# 编码的这些年中,我从未想过在 C# 中可以像这样违反安全内存访问,而无需显式启用不安全代码。

考虑一下您的示例的这种变化:

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;
    }
}

您会发现,在最后一条语句中,text变量已设置为"object A Tag"。换句话说,您的“覆盖”方法允许您的代码重新解释对 class 对象的A引用,作为对 class 对象的引用Overlay,而根本没有任何类型的编译器警告或运行时错误。

在上面的示例中,结果与您希望的一样好:原始属性值Text已从其正确值更改为作为“标签”传递的文本。这已经够糟糕的了,但在其他情况下,您可能会发现您的课程已经以可怕的方式损坏,导致进一步的数据损坏或(如果您幸运的话)由于某种访问冲突或其他异常导致您的程序立即终止.

不要这样做。这是非常危险的,当然,当以您在此处提出的方式使用时,将永远无法正常工作。你总是会覆盖一些你不应该拥有的数据。

于 2017-04-07T23:55:18.523 回答