2009-11-25 46 views
8

好吧,讓我們說我們有一個類像反射破壞封裝原理嗎?

public class TestClass 
{ 
    private string MyPrivateProperty { get; set; } 

    // This is for testing purposes 
    public string GetMyProperty() 
    { 
     return MyPrivateProperty; 
    } 
} 

定義,那麼我們嘗試:

TestClass t = new TestClass { MyPrivateProperty = "test" }; 

編譯失敗TestClass.MyPrivateProperty is inaccessible due to its protection level,符合市場預期。

嘗試

TestClass t = new TestClass(); 
t.MyPrivateProperty = "test"; 

和編譯再次失敗,使用相同的消息。

一切都很好,直到現在,我們期待着這一點。

但隨後一個寫:

PropertyInfo aProp = t.GetType().GetProperty(
     "MyPrivateProperty", 
     BindingFlags.NonPublic | BindingFlags.Instance); 

// This works: 
aProp.SetValue(t, "test", null); 

// Check 
Console.WriteLine(t.GetMyProperty()); 

和我們在這裏,我們設法改變私人領域。

僅僅通過使用反射就能改變某個對象的內部狀態不是不正常嗎?

編輯:

感謝迄今爲止的答覆。對於那些說「你不必使用它」的人來說:對於一個班級設計師來說,看起來他不能再承擔內部狀態安全了?

+0

只是使用反射?有人不僅僅使用反射,而是精確地知道他正在破壞API。 – 2009-11-25 10:46:25

回答

13

反思通過授予私人領域和方法的訪問權來打破封裝原則,但它並不是封裝可以繞過的第一種或唯一的方式;人們可能會爭辯說,序列化暴露了一個類的所有內部數據,通常是私有的信息。

瞭解封裝只是一種技術,讓設計行爲更容易,只要消費者同意使用您定義的API就很重要。如果有人選擇使用反射或任何其他技術繞過您的API,他們不再保證您的對象將按照您的設計行事。如果有人給私人領域分配null的值,他們最好準備在下次嘗試使用您的課程時抓到NullReferenceException

根據我的經驗,編程完全是關於斷言和假設。該語言斷言約束(類,接口,枚舉),這使得創建孤立行爲更容易產生,假設消費者同意不違反這些邊界。

這是一個公平斷言使它成爲一個分而治之的軟件開發方法比之前的任何技術更容易。

+1

雖然這個問題收到了其他優秀的答案,但我選擇這個是因爲重點放在了一個應該考慮封裝的事實上作爲許多其他(推薦)技術。 – 2009-11-26 12:42:36

8

反射是一種工具。你可能會用來破解封裝,當它給你的東西比它帶走的更多。

與它相關的反射有一定的「痛苦」(或代價 - 性能,可讀性,代碼可靠性),所以你不會將它用於常見問題。這只是更容易遵循面向對象的原則的常見問題,這是語言的目標之一,通常被稱爲the pit of success

另一方面,有些任務在沒有這種機制的情況下是不能解決的,例如,使用運行時生成類型(儘管從.NET 4.0開始,使用它的DLR和C#4.0中的「動態」變量會更容易)。

2

有人可以說,反射也會破壞繼承和多態性。當不是爲每個對象類型提供自己的重載方法版本時,您有一個方法在運行時檢查對象類型並切換到每個對象的特定行爲。

儘管如此,反射仍然是一個不錯的工具。有時你需要使用私有構造函數實例化一個對象,因爲這是一個最佳的操作過程,並且不能更改類實現(封閉的庫,不能再與作者聯繫)。或者您希望在一個地方對各種對象執行一些輔助操作。或者,也許你想爲某些類型執行日誌記錄。這些是反思確實有助於而不會造成任何傷害的情況。

2

這是由Reflection提供的功能的一部分,但我不會說,它的最佳用途。

它只適用於完全信任。

3

我想你可以這麼說。你也可以說CodeDom,Reflection Emit和Expression Tree API通過允許你即時編寫代碼來打破封裝。它們只是.NET提供的工具。你不必使用它們。

不要忘記,你必須(一)在完全信任運行,和(b)明確指定BindingFlags.NonPublic,爲了訪問私人數據。這應該足夠安全網。

2

不,它不是異常。
這是反射允許你做的事情,在某些情況下,你做了什麼可能會有一些用處,在許多其他情況下,只會讓你的代碼成爲定時炸彈。

反射是一種工具,它可以被使用或濫用。


關於編輯後的問題:答案根本不是。 一個API設計者可以盡他所能,付出很大的努力來暴露一個乾淨的界面,並通過反射看到他的API被過度濫用。

工程師可以建立一個完美的洗衣機,它是安全的,不會對電力造成衝擊等,但如果用錘子敲擊它直到看到電纜,那麼沒有人會責怪工程師得到一個衝擊:D

+0

哈哈,洗衣機...所以封裝帶有保修證書,你說的是)? – 2009-11-25 14:12:32

+0

Hehehe ...也許帶着合同:P – 2009-11-25 14:41:49

2

封裝原則是由您的API保留,但反射給你一種方法來解決API。

使用反射的人知道他沒有正確使用你的API!

+0

非常清晰易記:)... – 2009-11-25 10:50:15

1

反射可以幫助你保持你的代碼清潔。例如。當你使用休眠時,你可以直接將私有變量綁定到數據庫值,並且不必編寫不必要的setter方法,否則就不需要。從這個角度看,反射可以幫助你保持封裝。

3

你是正確的,反射可反對任何數量的良好的設計原則,但它也可以是一個重要組成部分,你可以用它來支持良好的設計原則 - 例如這可以通過插件,控制反轉等擴展軟件

如果你擔心它代表了應勸阻能力,你可能有一個點。但是使用真正的語言功能並不方便,所以以正確的方式做事更容易。

如果你認爲反射應該是不可能,你在做夢!

在C++中沒有像這樣的反射。但是編譯器生成的機器代碼使用訪問對象結構(和虛函數)的底層「對象模型」。所以一個C++程序員可以用相同的方式打破封裝。

class RealClass 
{ 
private: 
    int m_secret; 
}; 

class FakeClass 
{ 
public: 
    int m_notSecret; 
}; 

我們可以採取一個指針RealClass類型的對象,並簡單地將其轉換爲FakeClass和訪問「私人」的成員。

任何受限系統必須在一個更靈活的系統的基礎上實現,所以它總是可以繞過它。如果反射未作爲BCL功能提供,則有人可以使用不安全代碼將其添加到庫中。

在某些語言中,有些方法可以封裝數據,以便在語言內不可能使用來獲取除某些規定方式之外的數據。但是,如果你能找到逃避語言的方法,將永遠有可能作弊。一個極端的例子是在JavaScript中範圍變量:

(function() { 

    var x = 5; 

    myGetter = function() { return x; }; 
    mySetter = function(v) { x = v; }; 

})(); 

執行後,全局命名空間包含兩個功能,myGettermySetter,這是訪問的x價值的唯一途徑。 Javascript沒有任何反射能力以其他方式得到x。但它必須在某種主機解釋器(例如瀏覽器)中運行,所以操縱x當然有一些可怕的方法。插件中的內存損壞錯誤可能會造成意外!