2016-06-16 61 views
10

只是好奇,正在改變一個結構/值類型的大小在C#中的突破性變化?結構在內存佈局方面往往更加敏感,因爲改變它們直接影響到數組/其他結構的大小。在它使用的庫中的結構佈局被更改之後,是否有任何破解代碼的例子,無論是二進制還是源代碼?正在改變一個結構的大小在C#中的重大變化?

注意:通過「中斷」,我的意思是它根本無法編譯或者IL失效。因此,例如我不會認爲這是一個突破的變化:

// My.Library v1 
public struct MyStruct {} 

// My.Library v2 
public struct MyStruct { int _field; } 

// App code 
using My.Library; 
using System.Runtime.InteropServices; 

Console.WriteLine(Marshal.SizeOf<MyStruct>()); // before printed 1, now prints 4 

因爲它仍然運行。

+1

您是否使用InteropServices與非託管代碼進行交互?如果是的話,答案是肯定的,這是一個突破性的改變。如果否,答案會更加細緻。 –

+0

對不起,我不明白你的問題...你重新編譯了大會,所以一切都應該沒問題? –

+0

抖動隱藏了很多罪惡。結構大小在構建時不起作用,僅在運行時發揮作用。有未初始化的字段可能會令人驚訝。 –

回答

8

通過添加字段來更改大小對於嚴格管理的代碼是可以的。

添加字段是不間斷的更改,因爲代碼將與新類型重新進行JIT編輯,並且所有分配都將使用正確的大小。由於它是值類型,所以新字段無論如何都將使用空值正確初始化。

刪除/更改現有字段或屬性的類型肯定會發生重大變化。

值類型是密封的 - 所以沒有其他庫可以從該類型派生 - 因此不像其他類那樣,它們不能創建「這個派生類沒有實現新的虛擬屬性/接口方法」的問題。

注意:如果值類型用於互操作或任何其他類型的二進制序列化,而不是任何更改正在打破的控制。

I.e.其他人使用MyLib.Point {int x;int y;}將二進制序列化的點列表保存到文件中。如果現在「MyLib」向MyLib.Point添加了一個新字段,則序列化的數據不再能用二進制序列化讀取。與本地互操作類似的問題。

3

是的,即使在嚴格託管的代碼中,如果添加新字段,源代碼不兼容性也是可能的。以你的例子,這與編譯版本1,但不是版本2:

MyStruct s; 
Console.WriteLine(s); 

的原因是,C#允許,如果所有字段已經被賦值一個struct本地使用。在版本1中,沒有字段,所以s是「明確分配」的。但是,如果在版本2中添加一個字段,即使它是私有的,也不會再編譯,因爲s不再明確分配。

這種情況應該是二進制兼容的,因爲CLR保證字段初始化爲默認值。

Jared Parsons對結構中私有字段的主題有good blog post,他詳述了更改私有實現細節會危險(對於不安全的代碼)或破壞的其他情況。

+0

Upvoted。順便感謝博客文章,是一個有趣的閱讀。 –