2017-04-07 56 views
2

我曾经写过一张code to add a name to a 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; 
     } 
    } 
} 

回答

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"。换句话说,您的“覆盖”方法允许您的代码重新解释对类A的对象的引用,作为对类Overlay的对象的引用,根本没有任何类型的编译器警告或运行时错误。

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

不要这样做。这是非常危险的,当然当按照你在这里提出的方式使用时,从来没有工作正常。你总是会覆盖你不应该拥有的一些数据。

+0

今天晚上我花了一些时间看这个问题,并发现了一些有趣的事情:改变'Tagger'为一个结构似乎迫使CLR将数据写入Overlay结构共享的对象的末尾,那里显然不好。同样,如果原始对象是一个'int []'的数组,并且一个'int'被写入'Tagger',则从起始指针开始的64字节的内存中会包含一些连续的数字(一个int [3]这个数字是+60)从第一个整数,但如果写了其他任何东西,该数字是相同的第一个整数...这真的很酷。 – Scott