How to choose between private and protected access

2020-04-01 08:08发布

问题:

I am trying on a project to use private values in my internal functions. In past I used only public ones, but I noticed that obfuscation is working much better when using as much as possible private parameters.

My question is regarding Parent/Child classes.

In my main class I define all the parameters as following :

public class MyFatherClass
{
    private long id = -1;
    public long ID { get { return this.id; } set { this.id = value; } }    
    ...
}

So in all internal functions I access to my private value instead of the public one.

Then in my daughter class I just add parameters specific to the child class.

public class MyChildClass : MyFatherClass
{
    private long anotherParameter = -1;
    public long AnotherParameter { get { return this.anotherParameter; } set { this.anotherParameter = value; } }    
    ...
}

Just, I see that in my Parent class, I can access to id and ID without problem, but from daughter classes I can only access ID(as id is private).

If I understood correct, I would need to replace all private by protected in my parent lass, so it would solve the problem? What I don't understand is the code is working even if I leave it so. Why don't I have an error message, when I set ID value in daughter class, the sentence this.id=value is executed, but how can can I access to it from my child class if it is private?

I am now hesitating, may I just add a private id in each child class, or may I set id to protected in my parent class?

Thanks for your explanations.

Edit, just adding a screenshot of my reversed code after obfuscation, so you could understand difference on how are obfuscated private/public methods/fields

回答1:

Why don't I have an error message, when I set ID value in daughter class, the sentence this.id=value is executed, but how can can I access to it from my child class if it is private?

When you call a public method on a class, that method can access private members of that class:

public class Foo
{
    public void Bar()
    {
        Baz();
    }

    private void Baz()
    {
        // private method called by public method
    }
}   

var foo = new Foo();
foo.Bar();

This compiles just fine. Your setter is the same: it's public, so callable from everywhere, even if it accesses private members.

As for making your field (private long id = -1;) protected: yes, that will mean you can access it in derived classes. But whether you want to is another question.

You have declared a public property for a reason. Perhaps you want to do some validation in its setter or getter. If not, if you're just using a property to access a private field, you could just ditch the entire private field and use an auto-implemented property:

public long ID { get; set; } = -1;

Then you can access the property everywhere, from within itself, from derived classes and from code using this class.

See also:

  • What is the difference between a field and a property?
  • What are Automatic Properties in C# and what is their purpose?


回答2:

Here is a short and reduced description of what access modifiers do:

  • Public : fields (variables) and properties (variables encapsulation) and methods (functions and procedures) are visible and accessible by the class itslef, by its childs and by any other external classes.

  • Private : members (fields, properties and methods) are visible and accessible only by the class, not by its childs nor by any external class.

  • Protected : members are visible and accessible by the class and by its childs, but not by others classes.

  • Internal : members are visible and accessible by the class and by its childs and by any class that is in the same assembly (.exe and .dll), but not by a class from another assembly.

So you should set id to protected in the parent class to use it in the childs.

But here is the rule:

  • If childs classes can modify id you should set as a protected field, and offer a public property (get) if available for external items.

  • If childs classes are not allowed to modify it you should set it private and offer :

    • A propected property with only a getter if external items can't access it.

    • A public property with only a getter if external items can access it.

Don't repeat a member with the same name else it will hide the parent and can cause polymorphism problems, else you know what you do.

You can read these tutorials to more understand access modifier keywords:

C# Access Modifiers

Access Modifiers (C# Reference)

Here are some readings:

C# Tutorial Level 0
C# Tutorial Level 1
C# Tutorial Level 2
C# Tutorial Level 3

C# Snippets @ Techi.io

Beginning Visual C# 2008 Programming



回答3:

The MyChildClass class which inherits from the MyFatherClass can not access the id field because it's private. To make it accessible, you will need to change the field's access modifier to either:

protected :

////////////////////////////////////
// Dll1.dll
////////////////////////////////////

namespace Dll1
{
    public class Base
    {
        //The field we are attempting to access
        protected int a;
    }

    public sealed class Main : Base
    {
        public void DoSomething()
        {
            //Can be done sins Main inherits from Base
            base.a = 100;
        }
    }

    public class Invader
    {
        public int GetA()
        {
            var main = new Main();

            main.DoSomething();

            // can not be done sins the field is only accessible by those that inherit from Base
            return main.a;
        } 
    }
}

////////////////////////////////////
// Dll2.dll
////////////////////////////////////

namespace Dll2
{
    public class ADll2Class : Dll1.Base
    {
        public int GetA()
        {
            //Can be done because ADll2Class inherits from Dll1's Base class
            return base.a;
        }
    }

}

private protected :

Same as protected but, in the example above, Dll2's class, ADll2Class, will not be able to access the a field because it would be privately protected, in other words only classes from the same dll as Base which inherit from Base will be able to access a.

or you can set it to

internal :

If the a field in the example above was internal, then, same as private protected, Dll2's class wont be able to access it but, the Invader class in Dll1 will be able to access it sins it's part of the same dll as Base.

Note that, sins you mentioned obfuscation, try as hard as you will, the id field can still be accessed by others in an obfuscated state with the help of reflection, especially sins you provide a public property ID, might as well set everything in your project to internal.