去年12月份,随着Visual Studio 2017 Update 15.5的发布,Visual C#迎来了它的最新版本:7.2. 在这个版本中,有个让人难以理解的新特性,就是private protected访问修饰符(Access Modifier)。至此,C#语言的访问修饰符有如下几种:框架
既然有了private和protected,那么private protected又是什么?它跟internal protected又有什么关系?本文简单介绍一下。ide
在解释private protected以前,首先让咱们回顾一下internal protected访问修饰符。internal protected表示,相同程序集(Assembly)中的其它类型,或者当前类的子类,具备访问该类中internal protected成员的能力,能够用下图表示:ui
在上图中:设计
所以,internal protected表示internal或者protected。对象
然而,private protected表示,仅有相同程序集(Assembly)中继承于当前类型的类,才能访问该类中private protected成员。换句话说,private protected就是访问者必须在相同程序集中(internal),同时还必须是被访问类型的子类(protected),能够用下图表示:blog
所以,private protected表示internal而且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的。也就是:get
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访问修饰符是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#语言版本。其中:
要使用private protected访问修饰符,则须要在此选择C# latest minor version (latest),或者C# 7.2.
本文对C# 7.2的新特性:private protected访问修饰符进行了解析,并经过案例来讲明它的应用场景以及Visual Studio 2017对于C#新特性的支持。