在写这篇文章的时候,C# 已经有了 17 年的历史了,可以必定地说它并没有去任何处所。C# 语言团队不停致力于开发新特性,改进开发人员的体验。
在这篇文章中,我在介绍 C# 历史版本的同时分享我最喜欢的特性,在强调实用性的同时展示其长处。
C#1.0 (ISO-1) 确实算是语言,却没有什么令人兴奋的,缺少许多开发人员喜欢的特性。仔细一想,我能说得出喜欢的只有一个特另外特性 - 隐式和显式接口实现 。
接口在现今开发 C# 的过程中仍然风行使用,以下面的 IDateProvider 接口为例。
public interface IDateProvider { DateTime GetDate(); }没有什么特另外,此刻着手两种实现方法 - 此中第一种是隐式实现,如下:
public class DefaultDateProvider : IDateProvider { public DateTime GetDate() { return DateTime.Now; } }第二种实现是如下的显式实现方法:
public class MinDateProvider : IDateProvider { DateTime IDateProvider.GetDate() { return DateTime.MinValue; } }注意显式实现如何省略访谒修饰符。别的,要领名称被写为 IDateProvider.GetDate() ,它将接口名称作为限定符的前缀。
这两件工作使得挪用更明确的。
显式接口实现的一个很好的方面是它强制消费者依赖于接口。显式实现接口的实例东西必需使用接口自己,而没有其他可用的接口成员!
但是,当您将其声明为接口或将此实现作为期望接口的参数通报时,成员将如预期可用。
这是出格有用的方面,因为它强制使用接口。通过直接使用接口,不会将代码耦合到底层实现。同样,明确的接口实现制止定名或要领签名歧义 - 并使单个类可以实现具有不异成员的多个接口。
Jeffery Richter 在他 CLR via C# 一书中提醒了我们显式的接口实现两个主要问题是值类型实例在投射到一个接口和明确实现的要领时将被装箱,同时不能被派生类挪用。
请记住,装箱和拆箱会影响性能。任何编程中,你应该评估用例来确保善用工具。
C# 2.0作为参考,我将列出C# 2.0 (ISO-2) 的所有特性。
匿名要领
协变和逆变
泛型
迭代器
可空类型
部分类型
我最在最喜欢 泛型 还是 迭代器 之间的摇摆,对我来说这是一个非常困难的选择,最终还是更喜欢泛型,趁便说说此中缘由。
因为对比于写迭代器,我更频繁地使用泛型。在 C# 中很多 SOLID 编程原则都是使用泛型来强化的,同样它也有助于连结代码的 干爽。不要误解我的意思,我同时也写了一些迭代器,在 C# 同样中值得给与!
让我们更详细地看看泛型。
编者注:学习如何 在 C# 中 使用泛型来提高应用措施的可维护性
泛型向.NET Framework引入了类型参数的观点,这使得可以设计类和要领来推迟一个或多个类型的规范,直到类或要领被客户端代码声明和实例化为止。
让我们想象一下,我们有一个名为 DataBag 的类,作为一个数据包。它可能看起来像这样:
public class DataBag { public void Add(object data) { // omitted for brevity... } }起初看起来这似乎是一个很棒的想法,因为你可以在这个 DataBag 的实例中添加任何对象。但是当你真正想到这意味着什么的时候,会感受相当骇人。
所有添加的内容都隐式地包装为 System.Object 。别的,如果添加了值类型,则会产生装箱。这些是您应该注意的性能考虑事项。
泛型解决了这一切,同时也增加了类型安适性。让我们改削前面的例子,在类中包罗一个类型参数 T ,并注意要领签名的变革。
public class DataBag { public void Add(T data) { // omitted for brevity... } }例如此刻一个 DataBag 实例将只允许挪用者添加 DateTime 实例。要类型安适,没有装箱或拆箱 ... 让更美好的工作产生。
泛型类型参数也可以被约束。通用约束是强有力的,因为它们必需遵守相应的约束条件,只允许有限范畴的可用类型参数。
有几种编写泛型类型参数约束的要领,请考虑以下语法:
public class DataBag where T : struct { /* T 值类型 */ } public class DataBag where T : class { /* T 类、接口、委托、数组 */ } public class DataBag where T : new() { /* T 有无参结构函数 */ } public class DataBag where T : IPerson { /* T 担任 IPerson */ } public class DataBag where T : BaseClass { /* T 派生自 BaseClass */ } public class DataBag where T : U { /* T 担任 U, U 也是一个泛型参数 */ }