(C#7.2)“私有保护”修饰符的用例是什么?


70

C#7.2引入了private protected修饰符

我一直使用属性来保护对字段的访问,允许通过Get / Set方法进行访问,因为我通常不希望通过自己的类以外的任何东西来修改对象的内部状态。

我试图理解为什么C#语言团队添加了此功能。在Google上进行了广泛的搜索之后,阅读并观看了“新消息”媒体(我已经看过Mads Torgerson的新闻稿详细信息视频),但我仍然不是一个明智的选择。

在我看来,这似乎使开发人员可以打破Liskov替代原则,但这可能是因为我不理解为什么现在存在此功能。

我知道如何使用它,而不是为什么-请有人可以提供真实的用法示例,而不是MSDN文档中人为的示例吗?


4
您确定您了解此修饰符的实际作用吗?
Evk

1
protected与之非常相似,但有一个重要区别:如果一个类是从它派生的,但在另一个程序集中,则它不能访问该成员,而protected不仅限于同一程序集。
蒂姆·施密特

6
这里是哪里我打算使用它,如果它的任何帮助的例子:github.com/GoogleCloudPlatform/google-cloud-dotnet/blob/master/...
乔恩斯基特

1
@Evk:因为我确实想直接在Query中实例化它。Query可以是一个具体的类,也可以有一个子类-但只能在同一程序集中。其他代码无法创建实例Queryprivate protected会给我正是我想要的。
乔恩·斯基特

2
有关此方面的一些想法,请参阅blogs.msdn.microsoft.com/ericlippert/2008/04/24/…。有一项原因使该功能花了十五年的时间才能使其足够接近要实现的优先级列表的顶部:这不是一个非常引人注目的,有趣或有用的功能
埃里克·利珀特

Answers:


77

在C#7.2之前,我们有protected internal修饰符。这实际上意味着受保护的OR内部,即-成员A可被子类以及当前程序集中的任何类访问,即使该类不是该类的子类A(因此,放宽了“ protected”所隐含的限制)。

private protected真正意味着受保护且内部的。也就是说,-成员仅可用于同一程序集中的子类,而不能用于外部程序集的子类(因此,“保护”所隐含的限制会更严格-变得更加严格)。如果您在程序集中构建类的层次结构并且不希望其他程序集中的任何子类访问该层次结构的某些部分,则这很有用。

我们可以举Jon Skeet在评论中提供的示例。假设你有课

public class MyClass {

}

而且您只希望能够在当前程序集中从其继承,而又不想允许直接实例化此类,除非从此类层次结构中进行实例化。

内部构造器可以实现仅在当前程序集中进行继承

public class MyClass {
    internal MyClass() {
    }
}

除了使用当前的类层次结构之外,防止直接实例化可以通过受保护的构造函数来实现:

public class MyClass {
    protected MyClass() {
    }
}

并获得两者-您需要private protected构造函数:

public class MyClass {
    private protected MyClass() {
    }
}

1
@TimSchmelter在某种意义上是相似的,它们都涉及对子类(受保护)和当前程序集(内部)的限制。他们只是以不同的方式(和vs或)组合了这些限制。
Evk

抱歉,删除我的评论,因为基于意见。但是我仍然认为它protected比“更相似” protected internal(因为公共汇编明智,这使访问修饰符非常混乱)。该protected访问修饰符具有相同的合同:只源于此相同类型或类型内访问。但是它的范围并不限于与该组件有关private internal。因此,如果成员是,protected但必须避免需要从该程序集外部访问它private protected
蒂姆·施密特

因此,通过阅读MSDN页面,我对此非常了解。我没有得到的是为什么您要按照它们的描述进行操作-这是在不引入属性的情况下从派生类(在同一程序集中)访问基类上的字段。如果该示例保持原样,那么它是很合理的(我喜欢您描述受保护的内部保护与私有受保护的保护的区别的方式)。也许我只是对MSDN示例有偏见!
杰伊(Jay)

@Jay好吧,您不应期望MSDN示例带来太多现实意义(据我了解,您的主要抱怨是他们使用的是受保护字段,甚至与它是受保护的还是“受私有保护的”都没有关系)。毕竟,这不是最佳设计实践的例子-仅说明“私有保护”的作用。
Evk

1
@RuudLenders:这个争论持续了很长时间;我本人是为“内部”而坚持,但是出于某种原因被认为令人讨厌。邀请您和其他所有人参与设计过程;没有人能提出更好的建议,显然您也没有。有关讨论,请参见roslyn.codeplex.com/discussions/541194
埃里克·利珀特

19

对于双字访问修饰符,我有这个概念-第一个访问器与另一个程序集相关,第二个访问器与定义它的那个程序集相关。

受保护的内部

  • 在另一个程序集中受保护:只能在子类中访问。

  • 当前程序集中的内部:当前程序集中的每个人都可以访问。

私人保护

  • 在另一个程序集中是private:不可访问。
  • 在当前程序集中受保护:仅在子类中可访问。

1
超级好,真的很容易,而且很有意义。它帮助我了解了所有这些令人困惑的事情。谢谢!
安德鲁·达什科夫

8

假设您有一个内部类SomeHelper,您希望将其用作公共抽象基类的实现的一部分:

public abstract class Test
{
    // Won't compile because SomeHelper is internal.
    protected SomeHelper CreateHelper()
    {
        return new SomeHelper();
    }

    public int Func(int x)
    {
        var helper = CreateHelper();
        return helper.DoSomething(x);
    }
}

internal class SomeHelper
{
    public virtual int DoSomething(int x)
    {
        return -x;
    }
}

由于您无法使用受保护的方法返回内部类型,因此无法编译。您唯一的办法就是不要SomeHelper以这种方式使用或SomeHelper公开。

(您可以创建SomeHelper一个受保护的内部类Test,但是如果SomeHelper打算供非该基类派生的其他类使用,则该类将无效。)

随着功能的引入private protected,您可以这样声明CreateHelper()

private protected SomeHelper CreateHelper()
{
    return new SomeHelper();
}

现在它将编译,您不必公开内部信息。


1
您可以delcareSomeHelper作为的protected内部类Test,然后它将进行编译(关于“您的唯一资源...”)。
Evk

@Evk啊,是的,我要补充一点(但是SomeHelper,那当然不是在使用“那样”),这就是我真正的意思。
马修·沃森

这可能也非常有用
NSGaga-inactive于2008年

不是“也”。这确实是真实世界中的用法示例。多年来,将内部类型传递到我自己的层次结构受保护的方法中或从中返回内部类型一直是一个问题。解决方法是将这些方法标记为,internal而不是逻辑上的标记,protected并使用编码规则来不从层次结构外部访问它们。@Evk答案中提供的示例从未引起任何问题,因为它是通过abstract仅使用internal构造函数构造基类来解决的。
伊万·斯托耶夫

@IvanStoev注意,这不是我的示例,而是摘自现实世界中的Jon Skeet的项目。在这种情况下,基类不能是抽象的,因为您需要实例化它(但只能从此类和子类中实例化)。
Evk
By using our site, you acknowledge that you have read and understand our Cookie Policy and Privacy Policy.
Licensed under cc by-sa 3.0 with attribution required.