解析Visual C# 7.2中的private protected访问修饰符
去年12月份,随着Visual Studio 2017 Update 15.5的发布,Visual C#迎来了它的最新版本:7.2. 在这个版本中,有个让人难以理解的新特性,就是private protected访问修饰符(Access Modifier)。至此,C#语言的访问修饰符有以下几种:
- private
- protected
- public
- internal
- internal protected
- private protected
既然有了private和protected,那么private protected又是什么?它跟internal protected又有什么关系?本文简单介绍一下。
private protected是怎么回事
在解释private protected之前,首先让我们回顾一下internal protected访问修饰符。internal protected表示,相同程序集(Assembly)中的其它类型,或者当前类的子类,具有访问该类中internal protected成员的能力,可以用下图表示:

在上图中:
- 程序集A中的X类,可以访问A类中的Method方法
- 程序集A中的B类,可以重载A类中的Method方法,B类中的其它成员也可以访问A类中的Method
- 程序集B中的C类,可以重载A类中的Method方法,C类中的其它成员也可以通过base.Method()访问A类中的Method
- 程序集B中C类的Method方法重载了A类的Method方法,因此,internal关键字被去掉,于是,程序集B中的Y类,无法访问C类中的Method方法
因此,internal protected表示internal或者protected。
然而,private protected表示,仅有相同程序集(Assembly)中继承于当前类型的类,才能访问该类中private protected成员。换句话说,private protected就是访问者必须在相同程序集中(internal),同时还必须是被访问类型的子类(protected),可以用下图表示:

因此,private protected表示internal并且protected。
private protected何时使用
理论上讲,private protected完善了C#语言的封装性,提供了另一层级别的成员访问保护,听起来感觉让人费解又没什么用。那么,什么时候使用这个访问修饰符呢?现假设你正在设计一个框架,其中有个类,它提供对象存储功能,它的职责是保存给定的对象,而它的每一种实现都需要依赖于一个对象的序列化机制,比如:
public sealed class SerializationHelper
{
public string Serialze(object s)
{
using (var memoryStream = new MemoryStream())
{
var serializer = new XmlSerializer(s.GetType());
serializer.Serialize(memoryStream, s);
return Encoding.UTF8.GetString(memoryStream.ToArray());
}
}
} public abstract class DataStorage
{
private readonly SerializationHelper serializer = new SerializationHelper();
protected SerializationHelper Serializer => serializer;
protected abstract void SaveObject(object obj);
} public sealed class InMemoryDataStorage : DataStorage
{
private readonly List<string> serializedData = new List<string>();
protected override void SaveObject(object obj)
=> serializedData.Add(Serializer.Serialze(obj));
}
上面的代码中,SerializationHelper提供了一种将对象序列化成XML字符串的机制;DataStorage是所有对象数据存储的基类,它当然也为其子类提供了一个访问对象序列化器的方式。由于这个对象序列化器是提供给其子类调用的,因此,DataStorage中的Serializer属性是protected的。最后,InMemoryDataStorage继承了DataStorage,通过调用由基类提供的Serializer属性,实现了SaveObject方法。
整个实现当然没有问题。可是,通过审核所有类型的可见性,我们发现,我们不打算将SerializationHelper这个类暴露给外界,也就是不希望其它的程序集能够直接访问SerializationHelper类,于是,我们将它设置成internal的。也就是:
internal sealed class SerializationHelper
{
public string Serialze(object s)
{
using (var memoryStream = new MemoryStream())
{
var serializer = new XmlSerializer(s.GetType());
serializer.Serialize(memoryStream, s);
return Encoding.UTF8.GetString(memoryStream.ToArray());
}
}
}
好了,问题来了,编译器开始抱怨了,说SerializationHelper类的访问级别比DataStorage.Serializer属性的访问级别要低:

道理显而易见:DataStorage.Serializer属性在DataStorage的子类中即可访问,这个子类可以是在DataStorage所在的程序集中,也可以是在另一个程序集中。然而,这个属性的依赖类型:SerializationHelper类,却只能在DataStorage所在的程序集中才能被访问。
于是,能量巨大的private protected闪亮登场。将DataStorage.Serializer属性的访问修饰符从protected改为private protected,问题就解决了:
internal sealed class SerializationHelper
{
public string Serialze(object s)
{
using (var memoryStream = new MemoryStream())
{
var serializer = new XmlSerializer(s.GetType());
serializer.Serialize(memoryStream, s);
return Encoding.UTF8.GetString(memoryStream.ToArray());
}
}
} public abstract class DataStorage
{
private readonly SerializationHelper serializer = new SerializationHelper();
private protected SerializationHelper Serializer => serializer;
protected abstract void SaveObject(object obj);
} public sealed class InMemoryDataStorage : DataStorage
{
private readonly List<string> serializedData = new List<string>();
protected override void SaveObject(object obj)
=> serializedData.Add(Serializer.Serialze(obj));
}
不过,一旦使用了private protected访问修饰符,DataStorage.Serializer属性就只能在DataStorage所在的程序集的子类中访问了。
private protected如何使用
private protected访问修饰符是C# 7.2的新特性。自从使用Roslyn编译器服务的C# 6.0开始,C#编译器的版本更新就可以与.NET Framework和Visual Studio的发布分离开来了。这一点在C# 7.x(包括7.1和7.2)的发布中逐步显现出来。在Visual Studio 2017的编译高级选项中,开发人员可以很方便地选择所需的C#版本:

如上图所述,在C#项目上点右键,在项目属性的Build标签页中,点击Advanced按钮,在Advanced Build Settings对话框中,通过Language version下拉框来选择所需的C#语言版本。其中:
- C# latest major version (default):C#最新的主版本,也就是与Visual Studio一起发布的主要版本,在VS2017上对应C# 7.0
- C# latest minor version (latest):C#的最新版,通常通过VS2017的升级包获得
要使用private protected访问修饰符,则需要在此选择C# latest minor version (latest),或者C# 7.2.
总结
本文对C# 7.2的新特性:private protected访问修饰符进行了解析,并通过案例来说明它的应用场景以及Visual Studio 2017对于C#新特性的支持。
解析Visual C# 7.2中的private protected访问修饰符的更多相关文章
- [译]C# 7系列,Part 5: private protected 访问修饰符
原文:https://blogs.msdn.microsoft.com/mazhou/2017/10/05/c-7-series-part-5-private-protected/ C#有几个可访问性 ...
- 关于public private protected访问修饰符
这个似乎都是老生常谈了,特别是找工作第一轮笔试的时候很爱考这些,再罗列一次,特别要注意继承的情况: 默认状态:即是不加修饰符的时候,所谓的default状态,在类内部可以被访问,在相同的包下面 ...
- Java 中的 protected 访问修饰符你真的了解吗?
protected Java 中的 protected 访问修饰符 总结 在同一个包中,类中 protected 或 default 修饰的属性或方法可以在类外被其对象 (实例) 外部访问,也可以被子 ...
- C#中的五个访问修饰符
一.public, private, protected, internal, protected internal 1.public : 公开的, 公共的 2.private : 私有的 (只能在当 ...
- 简述public private protected internal修饰符的访问权限
public 关键字是类型和类型成员的访问修饰符.公共访问是允许的最高访问级别.对访问公共成员没有限制. protected 关键字是一个成员访问修饰符.受保护成员在它的类中可访问并且可由派生类访问. ...
- 什么是封装? ----------------php中"public"类似的访问修饰符分别有什么作用?----什么是抽象?抽象的关键字以及用法----- 什么是接口?接口的关键字以及用法-------------
什么是封装? ------------------------------------封装是php面向对象的其中一个特性,将多个可重复使用的函数封装到一个类里面.在使用时直接实例化该类的某一个方法,获 ...
- Java中的protected访问修饰符
在某个类中定义的protected 方法和属性和默认权限方法和属性是一样的.比如,某类的protected 方法和属性在包外是不能通过该类实例进行访问的(你能在包外访问一个类的默认权限的方法和属性吗? ...
- 深入浅出OOP(五): C#访问修饰符(Public/Private/Protected/Internal/Sealed/Constants)
访问修饰符(或者叫访问控制符)是面向对象语言的特性之一,用于对类.类成员函数.类成员变量进行访问控制.同时,访问控制符也是语法保留关键字,用于封装组件. Public, Private, Protec ...
- java中抽象类与接口中方法访问修饰符问题 (
1.抽象类中的抽象方法(其前有abstract修饰)不能用private.static.synchronized.native访问修饰符修饰.原 因如下:抽象方法没有方法体,是用来被继承的,所以不能用 ...
随机推荐
- thinkpad E480 用户初体验
梦寐以求的E480终于到手了,经过几天使用之后,也该写一下用户体验了,也算是对这些天的调研的一个总结吧. 首先是外形,从外形上来看E480的外形的确是相对以前的塑料外壳,改进了不少,全金属的机身,磨砂 ...
- ios开发之自定义textView
自定义textView,从理论上讲很简单,根据需求自定义,比如我在开发中的需求就是现实一个字数的限制以及根据输入的文字改变提示剩余字数,那么开始我的基本思路就是自定义一个View,而里面包含一个子控件 ...
- 【其他】Objective-C 内存管理学习总结
转载请注明出处:http://www.cnblogs.com/shamoyuu/p/OC_CG.html 最近学习了Objective-C语言(以下简称OC),其他的都还好,唯有它的内存管理让我不知所 ...
- python网络编程基础知识整理
- Ubuntu的shell之bash和dash
Ubuntu的 shell 默认安装的是 dash,而不是 bash. 运行以下命令查看 sh 的详细信息,确认 shell 对应的程序是哪个: $ls -al /bin/sh dash 比 bash ...
- CWnd *和HWnd转换
CWnd *和HWnd转换 CWnd*得到HWnd CWnd wnd; HWND hWnd; hWnd = wnd.m_hWnd; // or ...
- Cookie操作类、压缩、序列化
1.cookie类 CartCookie.cs using System; using System.Data; using System.Configuration; using System.We ...
- Django学习-5-模板渲染
1. {{ 变量名 }} def func(request): return render(request, ...
- INT_MAX (2147483647) 和INT_MIN (-2147483648)溢出
c语言中32位int型数据在运算的时候可能会出现溢出的情况,如: -2147483648-1会得到什么结果? -2147483648乘(-1)会得到什么结果? 2147483647+1会得到什么结果? ...
- Xshell配色为ubuntu风格
背景 为了远程连接服务器,用Xshell作为连接工具,因为好(mian)用(fei),服务器是ubuntu的,因此看不习惯Xshell自带的黑白色,下面给出了ubuntu的配色方案,使用的时候直接导入 ...