2009-09-18 56 views
31

我想隱藏基本公共財產(數據成員)在我的派生類:如何隱藏在派生類的基類的公共財產

class Program 
{ 
    static void Main(string[] args) 
    { 
     b obj = new b(); 
     obj.item1 = 4;// should show an error but it doent ??? 
    } 
} 

class a 
{ 
    public int item1 {get; set;} 
    public int item2 { get; set; } 
} 

class b : a 
{ 
    new private int item1; 
} 

class c : a 
{ 

} 

我有成員作爲公衆,因爲我想要的成員要在c類中繼承,但想隱藏b類中的成員,我該怎麼做?

不要我有選擇性地繼承我想在我的基類中的變量?這就是非常糟糕的,我認爲微軟應該爲我們提供了一個選項(可能是修改)來執行此


編輯:

,我發現自己的答案(我聽到很多人講這是不可能在C#中,但你可以種做)

我,包括情況下,代碼是非常有用

class Program 
{ 
    static void Main(string[] args) 
    { 
     b obj = new b(); 
     obj.item1 = 4; // shows an error :) 
    } 
} 

class a 
{ 
    public int item1 { get; set; } 
    public int item2 { get; set; } 
} 

class b : a 
{ 
    new public static int item1 
    { 
     get; 
     private set; 
    } 
} 
+0

此代碼是否正確編譯? – vpram86 2009-09-18 11:15:10

+0

我相信如果你告訴我們這個用例,還有一個替代解決方案。你爲什麼想這樣做? – 2009-09-18 11:21:56

+0

你的解決方案顯然只隱藏了setter。 雖然它停止立即指派,沒有什麼可以阻止下列: ((a)中OBJ).item1 = 4; B不能說ITEM1永遠不會改變,因爲它可能的假設下進行編程。它停止了簡單的情況,但並非每個案例。更健壯的解決方案是重構繼承層次結構。 – ICR 2009-09-18 13:18:41

回答

9

什麼你想直接違背面向對象的問題,你不能'取消'成員,因爲這違反了替代原則。你必須將其重構爲其他東西。

+0

所以艾菲爾得到這個錯誤,因爲它可以讓你''定義'基類的成員? – Joey 2009-09-18 11:21:24

+0

我不知道埃菲爾,但他們如何處理'a1 = new b()'? – 2009-09-18 11:23:06

+0

啊,我想我的錯誤。你可以定義一些東西,但是顯然你需要在類中有一個同名的特徵。但是對於多重繼承,它不是很清楚它來自哪裏(特別是當發生名稱衝突時)。 – Joey 2009-09-18 11:25:51

0

您所描述的內容類似於C++中的「私有繼承」,並且在C#中不可用。

4

我能想到的唯一的事情就是讓在課堂上物品1虛擬:

class a 
{ 
    public virtual int item1 { get; set; } 
    public int item2 { get; set; } 

} 

,然後在類B改寫,但在getter和setter拋出異常。此外,如果此屬性用於視覺設計師,則可以使用Browsable attribute不顯示。

class b : a 
{ 
    [Browsable(false)] 
    public override int item1 
    { 
     get 
     { 
      throw new NotSupportedException(); 
     } 
     set 
     { 
      throw new NotSupportedException(); 
     } 
    } 
} 
+0

即使我這樣做,當我創建一個類b的實例,我不希望它有一個選項'item1' – ravikiran 2009-09-18 11:29:42

+1

使用Browsable屬性是他們如何在.NET Framework中執行此操作。 – ICR 2009-09-19 12:04:06

0

你不能直接做,但你可以重寫子類中的屬性並使它們只讀,例如,

class Program 
{ 
    static void Main(string[] args) 
    { 
     b obj = new b(); 
     obj.item1 = 4;// should show an error but it doent ??? 
    } 
} 

class a 
{ 
    public virtual int item1 {get; set;} 
    public virtual int item2 { get; set; } 

} 

class b : a 
{ 
    public override int item1 
    { 
     get { return base.item1; } 
     set { } 
    } 
} 

    class c : a 
{ 

} 
0

您可以使用接口來隱藏屬性。子類將實現一個沒有屬性的接口,然後它不會出現。

當你想要這個屬性,當你不需要時,你需要兩個接口,因此使它成爲一個可怕的黑客。

22

我將嘗試用示例來解釋爲什麼這是一個糟糕的主意,而不是使用神祕術語。

你的建議是有一些代碼看起來是這樣的:

public class Base 
{ 
    public int Item1 { get; set; } 
    public int Item2 { get; set; } 
} 


public class WithHidden : Base 
{ 
    hide Item1; // Assuming some new feature "hide" in C# 
} 

public class WithoutHidden : Base { } 

這將進行以下代碼無效:

WithHidden a = new WithHidden(); 
a.Item1 = 10; // Invalid - cannot access property Item1 
int i = a.Item1; // Invalid - cannot access property Item1 

這將是你想要什麼。但是,假設我們現在有下面的代碼:

Base withHidden = new WithHidden(); 
Base withoutHidden = new WithoutHidden(); 

SetItem1(withHidden); 
SetItem1(withoutHidden); 

public void SetItem1(Base base) 
{ 
    base.Item1 = 10; 
} 

編譯器不知道SetItem1論證的基礎是什麼運行時類型,只知道它是至少型基地(或某些類型的從基地派生,但它無法分辨出 - 看代碼片段可能很明顯,但更復雜的場景幾乎不可能)。

因此,編譯器在很大比例的情況下不能給出編譯器錯誤,實際上Item1實際上是不可訪問的。這樣就留下了運行時檢查的可能性。當您嘗試在事實上類型爲WithHidden的對象上設置Item1時,它會拋出異常。

現在訪問任何成員,任何非密封類(其中大多數)的任何屬性可能會拋出異常,因爲它實際上是一個隱藏成員的派生類。任何暴露任何非密封類型的圖書館在訪問任何成員時都必須編寫防禦性代碼,因爲有人可能隱藏了它。

一個潛在的解決方案是編寫該功能,以便只有聲明自己可隱藏的成員才能隱藏。然後,編譯器將不允許對該類型變量(編譯時)的隱藏成員進行訪問,並且還包含運行時檢查,以便在將其轉換爲基本類型並試圖從該類型(運行時)訪問時引發FieldAccessException。 。儘管C#開發者確實遇到了這個特性的巨大麻煩和費用(請記住,特性昂貴的,特別是在語言設計中),仍然必須編寫防禦性代碼以避免潛在的FieldAccessExceptions存在的問題拋出,所以你獲得了什麼優勢而不是重組你的繼承層次結構?有了新的成員隱藏功能,就會有大量潛在的漏洞涌入您的應用程序和庫中,從而增加開發和測試時間。

+0

儘管基類的'public'成員通常應該在派生類中可見,許多類繼承了'protected'成員,這些成員在派生類中不應該是可見的。舉例來說,我認爲99%的類將「MemberwiseClone」暴露給子派生類,但在大多數情況下(包括'List '和'Dictionary ',這兩者都可以從'Clone'中受益)方法),子派生類不可能使'MemberwiseClone'有效地工作,因此應該隱藏成員。用'Obsolete'方法替換也許是最好的,但是... – supercat 2015-04-03 21:30:50

+0

...我幾乎從來沒有看到它完成。 – supercat 2015-04-03 21:31:07

4

Vadim的回覆讓我想起了MS在某些地方如何在框架中實現這一點。一般策略是使用EditorBrowsable attribute隱藏Intellisense中的成員。 (注意,如果它在另一個程序集中,它只會隱藏它)。雖然它不會阻止任何人使用該屬性,並且如果它們轉換爲基本類型,他們可以看到它(請參閱我之前的發現),但它使得它不易發現不會出現在Intellisense中並保持類的界面清潔。

它應該謹慎使用,但只有在重構繼承層次結構等其他選項會使其更復雜時纔會使它成爲批次。這是最後的手段,而不是首先想到的解決方案。

1
namespace PropertyTest  
{  
    class a 
    {  
     int nVal; 

     public virtual int PropVal 
     { 
      get 
      { 
       return nVal; 
      } 
      set 
      { 
       nVal = value; 
      } 
     } 
    } 

    class b : a 
    { 
     public new int PropVal 
     { 
      get 
      { 
       return base.PropVal; 
      } 
     } 
    } 

    class Program 
    { 
     static void Main(string[] args) 
     { 
      a objA = new a(); 
      objA.PropVal = 1; 

      Console.WriteLine(objA.PropVal); 

      b objB = new b(); 
      objB.PropVal = 10; // ERROR! Can't set PropVal using B class obj. 
      Console.Read(); 
     } 
    } 
} 
+1

請寫一些簡短的文字,解釋爲什麼這會解決OP的問題。 – Spontifixus 2012-11-27 18:39:50

-1

您可以覆蓋它,然後添加[Browsable(false)]標記以防止在設計器中顯示它。

簡單:

public class a:TextBox 
{ 
     [Browsable(false)] 
     public override string Text 
     { 
      get { return ""; } 
      set { } 
     } 
} 
+1

這是讓Api的最終用戶使用VS的推測。 – garfbradaz 2015-12-28 10:56:14

0

更改虛擬成員的可訪問是繼承類特別地由C#語言規範禁止:

的重寫聲明和被覆蓋的基體的方法具有相同的 宣佈可訪問性。 換句話說,重寫聲明不能 更改虛擬方法的可訪問性。然而,如果 重寫的基方法是受保護的內部和它被聲明在 不同組件比含有重寫方法 則重寫方法的聲明可訪問必須被保護的組件。

從部分10.6.4覆蓋方法

適用於重寫的方法同樣的規則也適用於性能,通過從基類繼承不能在C#完成從public所以要private

+0

由於沒有澄清這個效果,我想我應該加上這個改變無法完成的強有力的理由。 – 2013-05-24 15:22:31

4

如果您使用的接口,而不是用於定義屬性的基類,你可以明確地執行的財產。這將需要明確的轉換到接口來使用該屬性。

public interface IMyInterface 
{ 
    string Name { get; set; } 
} 

public class MyClass : IMyInterface 
{ 

    string IMyInterface.Name { get; set; } 

} 

您可以在此找到更多here

+0

我發現這是將所有內容都包含到單個類定義中的好方法,並且通過命名消費者可能不應該將其轉換爲接口(例如,像'IMyFactoryInterface' – MechEthan 2018-01-20 19:55:41

1

這一切首先是不是好主意,如果你使用一些方法,其操作的基類。
您可以嘗試使用過時的參數讓用戶兩次認爲使用此屬性。

[System.Obsolete("Do not use this property",true)] 
public override YourType YourProperty { get; set; } 
0

如果你想隱藏基類成員,那麼你將需要添加一個新的基類我們稱之爲baseA和你的代碼應該如下:

類節目 { 靜態無效的主要(string [] args) {obj = obj = new b(); obj.item1 = 4; //應該顯示一個錯誤,但它會顯示? }}

class baseA 
{ 
    public int item2 { get; set; } 
} 
class a:baseA 
{ 
    public int item1 { get; set; }   
} 

class b : baseA 
{   
} 

class c : a 
{ 
} 
0

你真正需要的是接口:

public interface ProvidesItem1 
    { 
     int item1 { get; set; } 
    } 

    public interface ProvidesItem2 
    { 
     int item2 { get; set; } 
    } 

    class a : ProvidesItem1, ProvidesItem2 
    { 
     public int item1 { get; set; } 
     public int item2 { get; set; } 
    } 

    class b : ProvidesItem1 
    { 
     public int item1 { get; set; } 
    } 

然後,只需繞過接口。如果這些類應該使用一個通用的實現,請將它放在第三個類中,讓它們從該類派生,並實現它們各自的接口。

0

是的,這是可能的。代表團對你說了什麼?我將嘗試通過一段代碼給出OOP中所謂的「委託」的概念:

public class ClassA 
{ 
    // public 
    public virtual int MyProperty { get; set; } 

    // protected 
    protected virtual int MyProperty2 { get; set; } 
} 

public class ClassB 
{ 
    protected ClassC MyClassC; 

    public ClassB() 
    { 
     MyClassC = new ClassC(); 
    } 

    protected int MyProperty2 
    { 
     get { return MyClassC.MyProperty2; } 
     set { MyClassC.MyProperty2 = value; } 
    } 

    protected int MyProperty 
    { 
     get { return MyClassC.MyProperty; } 
     set { MyClassC.MyProperty = value; } 
    } 

    protected class ClassC : ClassA 
    { 
     public new int MyProperty2 
     { 
      get { return base.MyProperty2; } 
      set { base.MyProperty2 = value; } 
     } 

     public override int MyProperty 
     { 
      get { return base.MyProperty; } 
      set { base.MyProperty = value; } 
     } 
    } 
} 
相關問題