我是荔园微风,作为一名在IT界整整25年的老兵,看到不少初学者在学习编程语言的过程中如此的痛苦,我决定做点什么,下面我就重点讲讲微软.NET6开发人员需要知道的C#特性,然后比较其他各种语言进行认识。
C#经历了多年发展, 进行了多次重大创新, 大幅优化了开发者的编码体验。在.NET 平台移交给.NET基金会运营后, C#更新的越来越不像原来的C#了,但总体上来说,所有改进依然以优化开发者的编码体验为最终目的。
首先,要记住一张表,如下:
C#版本 发布时间 .NET版本 VS版本 CLR版本
C#1.0 2002-2 .NET Framework 1.0 VS.NET 2002 .NET Framework CLR 1.0
C#2.0 2005-11 .NET Framework 2.0 VS2005 .NET Framework CLR 2.0
C#3.0 2006-11 .NET Framework 3.0 VS2008 .NET Framework CLR 2.0
C#3.0 2007-11 .NET Framework 3.5 VS2008 .NET Framework CLR 2.0
C#4.0 2010-4 .NET Framework 4.0 VS2010 .NET Framework CLR 4.0
C#5.0 2012-2 .NET Framework 4.5 VS2012 .NET Framework CLR 4.0
C#6.0 2015-7 .NET Framework 4.6 VS2015 .NET Framework CLR 4.0
C#7.0 2016-8 .NET Framework 4.6.2 VS2017(v15) .NET Framework CLR 4.0
C#7.1 2017-4 .NET Framework 4.7 VS2017(v15.3) .NET Framework CLR 4.0
C#7.2 2017-10 .NET Framework 4.7.1 VS2017(v15.5) .NET Framework CLR 4.0
C#7.3 2018-4 .NET Framework 4.7.2 VS2017(v15.8) .NET Framework CLR 4.0
C#8.0 2019-4 .NET Framework 4.8 VS2019(v16.3) .NET Framework CLR 4.0
C#8.0 2019-9 .NETCore 3.0 VS2019(v16.4) .NETCore CLR 3.0
C#9.0 2020-11 .NET 5.0 VS2019(v16.8) .NET CLR 5.0
C#10.0 2021-11 .NET 6.0 VS2022(v17) .NET CLR 6.0
看完这张表,我真的是很感慨,从测试版开始,我居然陪伴着.NET和C#走过了二十多年,我不知道有没有微软公司的人在看这篇文章,如果有的话,不知道我这样的二十多年的.NET和C#程序员有没有机会去微软中国和微软亚洲研究院的总部去参观一下,去坐一坐,并作一下技术交流。二十多年了,人生又有几个二十多年啊。
.NET平台是基于IL中间语言的应用运行环境,面向对象语言C#是平台的主要开发语言。除此之外还有同样面向对象的C++/CLI。C++/CLI主要用于和原生C++交互,在.NET平台中仅支持Windows系统。
C#和.NET平台本来是微软为了与Java平台竞争而打造的,C#在设计时充分总结了Java的经验教训,解决了大量Java的基本设计缺陷。本着为一线开发者谋实惠的宗旨,C#设计了大量能减轻开发者的编写负担、容易理解且安全高效的实用功能。为了尽可能降低因安全措施导致性能大幅下降的影响,C#还在有限的情况下保留了C/C++语言的部分语法和功能。到了.NET时代,微软依然在运行时(Runtime)和语言两边同时进行着优化。
随着上世纪九十年代Java的发布,软件公司和开发者开始感受到基于虚拟机的托管语言所带来的好处,微软也不甘示弱,在2001年发布了.NET Framework平台和C#。提供了完整的基础面向对象支持。
接口
接口是从C++开始出现的概念,用来表示不相关的类能拥有的共同特征。由于C++支持类的多重继承,因此直接用纯抽象类和纯虚函数来实现接口的功能,并且C++中没有abstract和interface关键字,是否是接口完全看类的定义是否符合接口规范。C#和Java都增加了专门的关键字来表达相应的概念。在C#和Java中,类只允许单继承且抽象类能够包含非抽象成员,接口可以多重实现但不允许包含具体定义,这样它们就变成了不可互相替代的功能。后来Java为接口添加了默认实现功能,隐约有了多重继承的功能,而C#也在8.0版跟进了这一功能。
接口定义了所有类继承接口时应遵循的语法合同。
接口定义了属性、方法和事件,这些都是接口的成员。接口只包含了成员的声明。成员的定义是派生类的责任。接口提供了派生类应遵循的标准结构。
接口使得实现接口的类或结构在形式上保持一致。
抽象类在某种程度上与接口类似,但是,它们大多只是用在当只有少数方法由基类声明由派生类实现时。
接口本身并不实现任何功能,它只是和声明实现该接口的对象订立一个必须实现哪些行为的契约。
抽象类不能直接实例化,但允许派生出具体的,具有实际功能的类。
声明接口:
接口使用 interface 关键字声明,它与类的声明类似。接口声明默认是 public 的:
public interface ITransactions
{
// 接口成员
void showTransaction();
double getAmount();
}
下面的实例演示了上面接口的实现:
using System.Collections.Generic;
using System.Linq;
using System.Text;
namespace InterfaceApplication
{
public interface ITransactions
{
// 接口成员
void showTransaction();
double getAmount();
}
public class Transaction : ITransactions
{
private string tCode;
private string date;
private double amount;
public Transaction()
{
tCode = " ";
date = " ";
amount = 0.0;
}
public Transaction(string c, string d, double a)
{
tCode = c;
date = d;
amount = a;
}
public double getAmount()
{
return amount;
}
public void showTransaction()
{
Console.WriteLine("Transaction: {0}", tCode);
Console.WriteLine("Date: {0}", date);
Console.WriteLine("Amount: {0}", getAmount());
}
}
class Tester
{
static void Main(string[] args)
{
Transaction t1 = new Transaction("001", "8/10/2012", 78900.00);
Transaction t2 = new Transaction("002", "9/10/2012", 451900.00);
t1.showTransaction();
t2.showTransaction();
Console.ReadKey();
}
}
}
接口的属性:
1,实现接口的任何类或结构必须实现其所有成员的方法。
2,接口不能直接实例化,但是可以通过指向子类间接实例化。
3,接口可以包含方法和属性的声明,但不能包含字段。
4,接口中所有方法、属性默认为public,不能在后面再添加修饰符。
5,类或结构可以实现多个接口。 类可以继承基类并实现一个或多个
接口。
接口的注意事项:
接口方法不能用public abstract等修饰。接口内不能有字段变量,构造函数。
接口内可以定义属性(有get和set的方法)。如string color { get ; set ; }这种。
实现接口时,必须和接口的格式一致。
必须实现接口的所有方法。
接口的特点:
接口的定义是指定一组函数成员而不实现成员的引用类型,其它类型和接口可以继承接口。定义还是很好理解的,但是没有反映特点,接口主要有以下特点:
(1)通过接口可以实现多重继承,C# 接口的成员不能有 public、protected、internal、private 等修饰符。原因很简单,接口里面的方法都需要由外面接口实现去实现方法体,那么其修饰符必然是 public。C# 接口中的成员默认是 public 的,java 中是可以加 public 的。
(2)接口成员不能有 new、static、abstract、override、virtual 修饰符。有一点要注意,当一个接口实现一个接口,这2个接口中有相同的方法时,可用 new 关键字隐藏父接口中的方法。
(3)接口中只包含成员的签名,接口没有构造函数,所以不能直接使用 new 对接口进行实例化。接口中只能包含方法、属性、事件和索引的组合。接口一旦被实现,实现类必须实现接口中的所有成员,除非实现类本身是抽象类。
(4)C# 是单继承,接口是解决 C# 里面类可以同时继承多个基类的问题。
接口与抽象类:
接口与抽象类非常相似,它定义了一些未实现的属性和方法。所有继承它的类都继承这些成员,在这个角度上,可以把接口理解为一个类的模板。接口最终的目的是起到统一的作用。
1,两者都包含可以由子类继承的抽象成员;2,两者都不直接实例化。3,抽象类除拥有抽象成员之外,还可以拥有非抽象成员;而接口所有的成员都是抽象的。4,抽象成员可以是私有的,而接口的成员默认是公有的。接口中不能含有构造函数、析构函数、静态成员和常量。5、接口支持多继承,抽象类不能实现多继承。6、接口只能定义抽象规则,抽象类既可以定义规则,还可能提供已实现的成员。7、接口是一组行为规范,抽象类是一个不完全的类,着重族的概念。8、接口可以用于支持回调,抽象类不能实现回调,因为继承不支持。9、接口只包含方法、属性、索引器、事件的签名,但不能定义字段和包含实现的方法,抽象类可以定义字段、属性、包含有实现的方法。10、接口可以作用于值类型和引用类型,抽象类只能作用于引用类型。例如,Struct就可以继承接口,而不能继承类。
在C#中,实现接口的方式分为隐式实现和显式实现。隐式实现就是直接在类中定义和接口声明相同的方法,隐式实现可以直接在对象上调用。显式实现需要在方法名前包含接口名,且不能使用访问修饰符,显式实现必须先把对象转换为接口类型才能调用。一个类可以同时定义隐式实现和显式实现。显式接口实现通常在多个接口有签名冲突的方法时使用。
使用C#、C++、Java实现接口的示例代码如下所示。
(1)C#
public interface MyInterface
{
void MyMethod();
int MyProperty { get; set;}
}
(2) C++
MyInterface.h
#pragma once
class MyInterface
{
public:
MyInterface(){ };
virtual ~MyInterface()=0{};
virtual void MyMethod()=0;
virtual int GetMyProperty()=0;
virtual void SetMyProperty(int value)=0;
};
MyInterface.cpp
#include "MyInterface.h"
(3)Java
package com.example. coredx. practice;
public interface MyInterface{
void myMethod();
int getMyProperty();
void setMyProperty (int value);
}
属性
属性(Property)是C#的独创功能,虽然属性在本质上就是方法(在C语言中称为函数),但却是拥有严格限制和特殊语法的方法。属性源于面向对象的封装性,意指类的字段(有时在C+中也被称为属性)不应该直接暴露到外部,要保持私有,应该由可以进行安全检查或其他额外处理的公共方法间接暴露到外部。因此,提供字段值的 get方法应该没有参数且返回类型和字段类型相同,修改字段值的set方法应该有一个和字段相同类型的参数且没有返回值。
比如要定义一个三角形类,三角形的三边长度存储在三个私有的数字字段中,就应该用属性对外提供访问渠道,因为三角形的边长是不能随意设置的,首先必须是正数,其次必须满足任意两边之和大于第三边,属性就能在赋值时提供相应的验证。另外三角形应该有周长属性,但周长实际是由三个边长间接算出来的,并不保存在字段中,除非要进行缓存避免重复计算,那缓存的失效和刷新就变成另一个要解决的问题了。这个时候,只读属性可以提供间接计算的功能并且和普通字段有相同的语法外观,用户也不必关心属性是从存储数据的字段中取出来的还是临时计算出来的。
C#则直接将属性变成语言本身的功能,并为其设计了专门的语法,后来又经过几次改进最终变成现在的样子。属性也彻底解决了Java的这一问题。C++也没有属性的概念,因此C+的基本解决方法和Java差不多,都是手动定义相应的访问函数。
使用C#、C++、Java定义一个三角形类的代如下所示。
(1)C#
using System;
namespace Example
{
public class Triangle
{
private double aa;
private double bb;
private double cc;
public Triangle(double a, double b, double c)
{
if (!Validate(a, b, c) throw new ArgumentException("三边长不满足三角形的规则。");
aa = a;
bb = b;
cc = c;
}
public double A
{
get { return aa; }
set {if (Validate (value, bb, cc)) aa=value; }
}
public double B
{
get { return bb; }
set {if (Validate (aa, value, cc)) bb=value; }
}
public double C
{
get { return cc; }
set {if (Validate (aa, bb, value)) cc=value; }
}
public double Perimeter
{
get{return aa + bb + cc;}
}
private bool Validate(double a, double b, double c)
{
return (a > 0 && b > 0 && c > 0)&&(a + b > c && a + c > b && b + c> a);
}
}
}
在C#的set访问器中,使用隐式参数关键字value表示属性赋值时的参数。
(2) C++
Triangle.h
#pragma once
class Triangle
{
private:
double aa;
double bb;
double cc;
bool validate(double a, double b, double c);
public:
Triangle (double a, double b, double c);
double GetA();
void SetA(double a);
double GetB();
void SetB(double b);
double GetC();
void SetC(double c);
double GetPerimeter();
};
Triangle.cpp
#include "Triangle.h"
#include <stdexcept>
using namespace std;
bool Triangle::Validate(double a, double b, double c)
{
return (a > 0 && b> 0 && c > 0)&& (a +b >c && a + c > b && b + c > a) ;
}
Triangle::Triangle(double a, double b, double c)
{
if (!Validate(a, b, c) throw invalid_argument("三边长不满足三角形的规则。");
aa= a;
bb= b;
cc= c;
}
double Triangle::GetA()
{
return aa;
}
void Triangle::SetA(double a)
{
if (Validate(a, bb, cc)) aa= a;
}
double Triangle::GetB()
{
return bb;
}
void Triangle::SetB(double b)
{
if (Validate(aa, b,cc)) bb= b;
}
double Triangle::GetC()
{
return c;
}
void Triangle::SetC(double c)
if (Validate(aa, bb, c)) cc= c;
}
double Triangle:: GetPerimeter()
{
return aa+bb+cc;
}
(3)Java
package com.example. coredx. practice;
public class Triangle{
private double aa;
private double bb;
private double cc;
public Triangle(double a, double b, double c) throws Exception{
if (!validate(a, b, c) throw new Exception("三边长不满足三角形的规则。");
aa = a;
bb = b;
cc= c;
}
public double getA()
{
return aa;
}
public void setA(double a)
{
if (validate(a, bb, cc)) aa= a;
}
public double getB()
{
return bb;
}
public void setB(double b)
{
if (validate(aa, b, cc))bb= b;
}
public double getC()
{
return cc;
}
public void setc(double c)
{
if (validate(aa,bb, c))cc= c;
}
private boolean validate(double a, double b, double c)
{
return (a > 0 && b > 0 && c > 0)&& (a + b > c && a +c > b && b + c >a);
}
}
作者简介:荔园微风,1981年生,高级工程师,浙大工学硕士,软件工程项目主管,做过程序员、软件设计师、系统架构师,早期的Windows程序员,Visual Studio忠实用户,C/C++使用者,是一位在计算机界学习、拼搏、奋斗了25年的老将,经历了UNIX时代、桌面WIN32时代、Web应用时代、云计算时代、手机安卓时代、大数据时代、ICT时代、AI深度学习时代、智能机器时代,我不知道未来还会有什么时代,只记得这一路走来,充满着艰辛与收获,愿同大家一起走下去,充满希望的走下去。