2017-04-07 72 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