如何在私有和受保护的访问修饰符之间进行选择以封装基类和子类之间的成员?

Posted

技术标签:

【中文标题】如何在私有和受保护的访问修饰符之间进行选择以封装基类和子类之间的成员?【英文标题】:How to choose between private and protected access modifier to encapsulate members between base and childs classes? 【发布时间】:2020-02-04 01:23:58 【问题描述】:

我正在尝试在我的内部函数中使用私有值的项目。过去我只使用公共参数,但我注意到当使用尽可能多的私有参数时,混淆效果会更好。

我的问题是关于父/子课程。

在我的主类中,我将所有参数定义如下:

public class MyFatherClass

    private long id = -1;
    public long ID  get  return this.id;  set  this.id = value;      
    ...

所以在所有内部函数中,我访问的是我的私有值而不是公共值。

然后在我的子类中,我只是添加特定于子类的参数。

public class MyChildClass : MyFatherClass

    private long anotherParameter = -1;
    public long AnotherParameter  get  return this.anotherParameter;  set  this.anotherParameter = value;      
    ...

只是,我看到在我的 Parent 类中,我可以毫无问题地访问 idID,但是从子类中我只能访问 ID(因为 id 是私有的)。

如果我理解正确,我需要将我父母的所有private 替换为protected,这样可以解决问题吗? 我不明白的是,即使我离开它,代码也能正常工作。 为什么我在子类中设置ID值时没有错误消息,执行了语句this.id=value,但是如果它是私有的,我如何从我的子类访问它?

我现在在犹豫,我可以在每个子类中添加一个private id,还是在我的父类中将id设置为protected?

感谢您的解释。

编辑,只是在混淆后添加我的反向代码的屏幕截图,这样您就可以了解如何混淆私有/公共方法/字段的区别

【问题讨论】:

首先,这些不是“参数”。 id 是“字段”,ID 是“属性”。 @KlausGütter 好的,请原谅我的语言不正确,我会问fieldproperty 有什么区别,但会进一步了解,谢谢。 类的私有成员在除类本身之外的任何地方都不可访问。即使在继承的类中也无法访问它们。如果您希望在继承的类中可以访问它们,则需要拥有受保护的成员。 "field" 是实际存储在对象实例中的内容。 “property”是一对 get-set 访问器的好语法。 你只需要创建那些你想在继承类中使用的成员protected。正如我所说,您可以访问基类的公共成员。因此,当在继承类中使用公共属性时,将执行该属性中编写的任何代码。它与您从类外部访问属性相同。 【参考方案1】:

为什么我在子类中设置ID值时没有错误消息,执行this.id=value这句话,但是如果它是私有的,我如何从我的子类访问它?

当您调用一个类的公共方法时,该方法可以访问该类的私有成员:

public class Foo

    public void Bar()
    
        Baz();
    

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

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

这编译得很好。你的 setter 是一样的:它是公共的,所以可以从任何地方调用,即使它访问私有成员。

至于保护您的字段 (private long id = -1;):是的,这意味着您可以在派生类中访问它。但你是否愿意是另一个问题。

您出于某种原因声明了公共财产。也许你想在它的 setter 或 getter 中做一些验证。如果没有,如果您只是使用属性来访问私有字段,则可以放弃整个私有字段并使用自动实现的属性:

public long ID  get; set;  = -1;

然后,您可以在任何地方访问该属性,从自身内部、派生类和使用此类的代码。

另见:

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

【讨论】:

我认为出于安全原因需要它?正如所解释的,因为我的混淆器每次使用它们时都会混淆所有私有字段,而不是公共字段。 this.ID=this.PublicParameter 没有被混淆,但this.id=this.privateParameter 会被混淆,所以如果我尝试反转代码,我只会看到kgkjkghllk=khgjgk,这就是我尝试在所有库中“私有化”的主要原因 不要编写糟糕的代码,这样混淆器会使情况变得更糟。如果你编写人们认为值得去混淆的代码,他们无论如何都会这样做。如果没有,你只是在浪费时间。 好吧,我可能在那之前问过...只是花了 3 天时间来修改我所有的代码...刚才我开始在儿童课上做 i【参考方案2】:

以下是对访问修饰符作用的简短描述:

Public:字段(变量)和属性(变量封装)和方法(函数和过程)对类本身、其子类和任何其他外部类都是可见和可访问的.

私有:成员(字段、属性和方法)只有类可见和可访问,其子类或任何外部类均不可见。

受保护:成员对类及其子类可见和可访问,但对其他类不可见。

内部:类及其子类以及同一程序集中的任何类(.exe 和 .dll)中的任何类(但不是类)都可以看到和访问成员来自另一个程序集。

所以你应该在父类中将id设置为protected,以便在子类中使用它。

但这是规则:

如果子类可以修改 id,则应将其设置为受保护字段,并提供公共属性 (get)(如果可用于外部项目)。

如果不允许子类修改它,您应该将其设置为私有并提供:

如果外部项目无法访问,则只有一个 getter 的预期属性。

如果外部项目可以访问它,则只有一个 getter 的公共属性。

不要重复具有相同名称的成员,否则它将隐藏父级并可能导致多态性问题,否则您会知道自己在做什么。

您可以阅读这些教程以进一步了解访问修饰符关键字:

C# Access Modifiers

Access Modifiers (C# Reference)

以下是一些读数:

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

C# Snippets @ Techi.io

Beginning Visual C# 2008 Programming

【讨论】:

【参考方案3】:

继承自MyFatherClassMyChildClass 类无法访问id 字段因为它是私有的。要使其可访问,您需要将字段的访问修饰符更改为:

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:

与 protected 相同,但在上面的示例中,Dll2 的类 ADll2Class 将无法访问 a 字段,因为它将受到私有保护,换句话说,只有来自与 @987654331 相同的 dll 的类继承自Base 的@ 将能够访问a

或者您可以将其设置为

internal:

如果上面示例中的a 字段是内部字段,那么,与private protected 相同,Dll2 的类将无法访问它,但 Dll1 中的Invader 类将能够访问它,因为它是它的一部分与Base 相同的dll。

请注意,你提到混淆的罪过,尽你所能,id字段仍然可以在reflection的帮助下被其他人以混淆状态访问,尤其是你提供公共属性@987654339的罪过@,不妨把你项目中的所有东西都设置成internal

【讨论】:

以上是关于如何在私有和受保护的访问修饰符之间进行选择以封装基类和子类之间的成员?的主要内容,如果未能解决你的问题,请参考以下文章

没有访问修饰符的字段,java [重复]

封装 继承 多态的区别

如何禁止公共继承但允许私有(和受保护)继承

访问修饰符封装继承

访问修饰符封装继承

2017-4-14访问修饰符 封装 继承 多态