简体   繁体   中英

c# hide from dervied classes some properties of base class

I want to know if its possible to hide a base class property from a derived class:

Example:

    class BaseDocument
    {
        public string DocPath{get; set;}
        public string DocContent{get; set;}
    } 

    class DerviedDocument: BaseDocument
    {
     //this class should not get the DocContent property
        public Test()
        {
           DerivedDocument d = new DerivedDocument();
           d.//intellisense should only show me DocPath
             //I do not want this class to see the DocContent property
        }
    }

I cannot make the DocContent property private, because I want to instantiate the BaseDocument class elsewhere and use the property there. That will kill the idea of a property anyway.

One way to fix this would be to use a interface, say IDoc, which exposes DocPath property and make both the BaseDocument and DerivedDocument implement the interface. This will break their parent-child relationship though.

I can play with the new and override keywords, but that's not the right way either because the child still 'sees' the property

I tried using the 'sealed' keyword on the DocContent, but that does not seem to solve the problem either.

I understand that it 'breaks' inheritance, but I guess this scenario should be coming up frequently where a child needs to get everything else from the parent but one or two properties.

How can such scenarios be handled gracefully?

I'm not sure inheritance would be the way to go here. Yes, you can hack around it by using the EditorBrowsableAttribute but I think the design should be rethought. One possible approach:

public interface IDoc
{
   DocPath{get;set;}
}

class BaseDocument : IDoc
{
     public DocPath{get; set;}
     public DocContent{get; set;}
} 

class DerviedDocument
{
    public DerivedDocument(IDoc doc)
    {
        this.Doc = doc;
    }

    public IDoc Doc{get;set;}

     public Test()
     {
        DerivedDocument d = new DerivedDocument(new BaseDocument());
        d.//here you will only see d.IDoc which only exposes DocPath

     }
}

Basically, use composition instead of inheritance, and program to an interface, not to an implementation.

You can do it easily if you don't mind having BaseDocument and DerivedDocument in different assemblies/projects.

Make DocContent internal. It'll be visible to everything in the same project as BaseDocument, but it won't be visible to DerivedDocument since that's in a different project. Of course, you'll need to make BaseDocument public (right now you have it as the default, internal).

In first project:

public class BaseDocument
{
    public string DocPath {get; set;}
    internal string DocContent {get; set;}
}

In second project that references first:

class DerivedDocument : FirstProject.BaseDocument
{
    public Test()
    {
       DerivedDocument d = new DerivedDocument();
       d.  //intellisense shows DocPath, but not DocContent
    }
}

This solution has the advantage of not being a kludge. You can still use BaseDocument's DocContent property within BaseDocument's project. If you need to use DocContent in another project (separate from the project DerivedDocument is in), you can use the InternalsVisibleTo attribute to make DocContent visible to that assembly. (That, however, is in my opinion a kludge, albeit a very handy one in some scenarios.)

It sounds like you want to intentionally violate the Liskov Substitution Principle. Why bother with subclassing at all if it's not going to have the conventional inheritance semantics? Just make a separate class.

interface IBaseDocument
{
    string DocPath    { get ; set ; }
    string DocContent { get ; set ; }
} 

class BaseDocument : IBaseDocument
{
    public string DocPath { get ; set ; } // implement normally

    private string MyDocContent ;   // use this in BaseDocument
    string IBaseDocument.DocContent // implement explicitly
    { 
        get { return MyDocContent  ; } 
        set { MyDocContent = value ; } 
    }
} 

class DerviedDocument : BaseDocument
{
    public void Test ()
    {
       // error: The name 'DocContent' does not exist in the current context
       Console.WriteLine (DocContent) ; 
    }
}

I don't believe there is a good (or any) way to do this. You may have to break the hierarchy, or you could remove the DocContent property from BaseDocument, then derive two sepearate classes from BaseDocument, one which is your current DerivedDocument, and another which has the DocContent property.

A late reaction, but there are several ways to do this.

Most beautiful: Place your Base class in a separate assembly and mark the property DocContent as internal instead of public :

class BaseDocument
{
    public string DocPath{get; set;}
    internal string DocContent{get; set;} //won't be visible outside the assembly
}

Or use attributes to hide the property from the source editor:

class BaseDocument
{
    public string DocPath{get; set;}
    public string DocContent{get; set;}
} 

class DerviedDocument: BaseDocument
{
   //this class should not get the DocContent property

  [Browsable(false), EditorBrowsable(System.ComponentModel.EditorBrowsableState.Never)]
  public new string DocContent{ get; set; }

  public Test()
  {
     DerivedDocument d = new DerivedDocument();
     d.//intellisense will only show me DocPath
     //I do not want this class to see the DocContent property
  }
}

Just do this.

class BaseDocument
{
    public DocPath{get; set;}
    public virtual DocContent{get; set;}
} 

class DerviedDocument: BaseDocument
{
    public override DocContent 
    { 
        get { return null; }
        set { } 
    }    
}

Or

public override DocContent 
{ 
    get { throw new NotImplementedException("Do not use this property!"); }
    set { throw new NotImplementedException("Do not use this property!"); } 
} 

The technical post webpages of this site follow the CC BY-SA 4.0 protocol. If you need to reprint, please indicate the site URL or the original address.Any question please contact:yoyou2525@163.com.

 
粤ICP备18138465号  © 2020-2024 STACKOOM.COM