2013-02-07 21 views
2

好了,所以可以說我有一個這樣的結構答:在C#中限制使用結構

Struct A{ 
    private String _SomeText; 
    private int _SomeValue; 

    public A(String someText, int SomeValue) { /*.. set the initial values..*/ } 

    public String SomeText{ get { return _SomeText; } } 
    public int SomeValue{ get { return _SomeValue; } } 
} 

現在我希望能夠做的是返回結構A作爲方法的結果在ABC類,像:

Class ABC{ 
    public A getStructA(){ 
     //creation of Struct A 
     return a; 
    } 
} 

我不使用我的媒體庫(這將有結構A和ABC類和一些更多的東西)永遠能夠創建一個結構的一個實例,希望任何程序員
我想要創建它的唯一方法就是從getS返回tructA()方法。然後可以通過適當的獲得者來訪問這些值。

那麼有什麼辦法可以設置這樣的限制嗎?所以一個Structure不能在某個類之外被實例化?使用C#,.Net4.0。

感謝您的幫助。

---編輯:----
要添加一些細節上,爲什麼我試圖做到這一點:

  • 我的ABC類有一些「身份」的人可以查詢。該狀態有2個字符串值,然後是一長串整數。
  • 從來沒有必要由程序員創建一個「狀態」對象/實例,狀態只能由類的「getStatus()」函數返回。
  • 我不想將這3個字段拆分爲不同的方法,以獲得它們我打電話給Windows API(p/invoke),它返回與所有3個字段相似的結構。
  • 如果我確實打算把它拆分3種方法,而不是使用結構,我就不得不要麼緩存結果,每一個的這3種方法被調用時調用從Windows API的方法...

所以我可以做一個公共結構和程序員可以實例化它,如果他們想,這將是無用的,因爲將不會有方法可以接受它作爲參數。或者我可以通過這樣的方式來構建庫,即只要該方法返回,就可以獲得該結構(如果它使事情變得更容易,則將其更改爲類)。

+3

否使用接口。 – SLaks

+1

你不能讓構造函數爲'internal'或'private'嗎? **編輯:**通過查找[this](http://stackoverflow.com/a/535782/298754)回答我自己的問題。答案是:「是的,但它不會幫助。」 – Bobson

+0

@丹尼爾這聽起來更像是一個設計問題。例如,如果您擔心用戶可能在調用某個方法時使用自己的'A',而不是您給出的方法,那麼您可能需要查看自己的設計,而不是使用結構。請你能解釋一下這個問題的背景嗎? –

回答

10

如果「受限制」類型是一個結構體,那麼沒有,沒有辦法做到這一點。該結構必須至少與工廠方法一樣公共,並且如果該結構是公共的,那麼它可以使用其默認構造函數構造。但是,您可以這樣做:

public struct A 
{ 
    private string s; 
    private int i; 
    internal bool valid; 
    internal A(string s, int i) 
    { 
     this.s = s; 
     this.i = i; 
     this.valid = true; 
    } 
    ... 

現在您可以讓您的庫代碼檢查「有效」標誌。 A的實例只能由(1)通過可以調用內部構造函數的庫內部方法,或者(2)通過默認構造函數進行。你可以用有效的標誌區分他們。

許多人建議使用接口,但這有點沒有意義;使用結構的關鍵在於獲取值類型的語義,然後將其裝入一個接口。首先,你可以讓它成爲一堂課。如果要成爲一個班級,那麼製造工廠方法肯定是可能的。只是讓班級的所有課程都內部化。

當然,我希望不用說這些設備都不應該被用來實現能夠抵抗完全可信用戶攻擊的代碼。請記住,這個系統是爲了保護好用戶錯誤代碼,而不是好代碼壞用戶。沒有什麼能阻止完全可信的用戶代碼通過反射來調用他們想要在你的庫中的任何私有方法,或者爲此,用不安全的代碼改變結構中的位。

+1

即使不受信任的代碼也可以通過濫用非原子分配來創建「invalid == true」結構的無效狀態。使用.net時,將安全性與低信任代碼混淆,以及防止意外濫用的安全性非常容易,因爲它支持兩者,並且受保護代碼中的差異相當微妙。 – CodesInChaos

+0

增加了我的用法等詳細信息到原來的帖子 –

+0

@CodesInChaos:由於這個原因等,我通常對結構嘗試執行不變量的概念持懷疑態度。在我看來,一個結構體*就是一堆粘在一起的領域的膠帶。儘管將結構僞裝成不可變類對象的輕量形式有時會很有用,但這種抽象通常是泄漏的。如果一個抽象是有用的(例如'Decimal'),那麼很好,但是我認爲在很多情況下,結構明確地說它的成員可能具有任何對它們的類​​型有效的值的組合是更好的... – supercat

0

創建一個公共接口,並使該類爲調用它的類的私有類。

public ISpecialReturnType 
{ 
    String SomeText{ get; } 
    int SomeValue{ get; } 
} 

class ABC{ 
    public ISpecialReturnType getStructA(){ 
     A a = //Get a value for a; 
     return a; 
    } 

    private struct A : ISpecialReturnType 
    { 
     private String _SomeText; 
     private int _SomeValue; 

     public A(String someText, int SomeValue) { /*.. set the initial values..*/ } 

     public String SomeText{ get { return _SomeText; } } 
     public int SomeValue{ get { return _SomeValue; } } 
    } 
} 
+0

當然,沒有什麼能阻止某人定義一個看起來和行爲像A一樣的'B:ISpecialReturnType',並且可以在任何地方使用'A'可以(如'A'私人的,所以你不能用'A'來定義方法合約,只能用'ISpecialReturnType'來定義)。在不知道他的要求的情況下,不可能知道這是否會成爲問題。 –

+3

這個解決方案當然也包含結構體。一個實現一個接口並始終裝箱的結構也可能是一個類。 –

0

你到底在關注什麼?結構基本上是用膠帶粘在一起的各個領域的集合。由於結構賦值將所有字段從一個結構實例複製到另一個結構實例,所以在結構類型控制之外,結構體執行任何類型的不變量的能力非常有限,特別是在多線程代碼中(除非一個結構完全正確1,2或4字節,想要創建包含從兩個不同實例複製的數據混合的實例的代碼可能非常容易,並且結構無法阻止它)。

如果您想確保您的方法不會接受除您的類型在內部生成的類型以外的任何其他類型的實例,則應該使用僅具有internalprivate構造函數的類。如果你這樣做,你可以確定你得到了你自己製作的實例。

編輯 根據修訂,我不認爲要求的限制類型是必要的或特別有用的。這聽起來像是將一堆值粘在一起並將它們存儲到由調用者持有的一組粘貼在一起的變量中從根本上想要的。如果您聲明一個結構作爲簡單:

public struct QueryResult { 
    public ExecutionDuration as Timespan; 
    public CompletionTime as DateTime; 
    public ReturnedMessage as String; 
} 

則聲明:

QueryResult foo; 

將有效地創建三個變量,命名爲foo.ExecutionDurationfoo.CompletionTimefoo.ReturnedMessage。聲明:

foo = queryPerformer.performQuery(...); 

將設置根據函數的結果這三個變量的值 - 本質上等同於:

{ 
    var temp = queryPerformer.performQuery(...); 
    foo.ExecutionDuration = temp.ExecutionDuration 
    foo.CompletionTime = temp.CompletionTime; 
    foo.ReturnedMessage = temp.ReturnedMessage; 
} 

沒有什麼會阻止用戶代碼做任何它想用那些三個變量,但那又如何?如果用戶代碼決定說明foo.ReturnedMessage = "George";,那麼foo.ReturnedMessage將等於George。這種情況是如果代碼已經說真的沒有什麼不同:

int functionResult = doSomething(); 

再後來說functionResult = 43;。像任何其他變量一樣,functionResult的行爲是保留寫入它的最後一件事情。如果寫入的最後一件東西是最後一次致電doSomething()的結果,那麼它將保持原樣。如果寫的最後一件東西是別的東西,它會包含其他東西。

請注意,與類字段或結構屬性不同,結構體字段可以通過寫入來更改,也可以通過使用結構賦值語句將一個結構體實例中的所有字段寫入值在另一個相應的字段中。從消費者的角度來看,只讀結構屬性沒有這樣的保證。一個結構可能碰巧實現一個屬性來表現這種行爲,但是如果沒有檢查屬性的代碼,就沒有辦法知道它返回的值是否會受到一些可變對象的影響。

+0

添加了我的用法等詳細信息到原始帖子 –

+0

@DanielGruszczyk:請參閱編輯。 – supercat