——
声明:本文内容整理自菜鸟教程、微软文档,内容有改动。
https://www.runoob.com/csharp/csharp-tutorial.html 菜鸟教程
https://docs.microsoft.com/zh-cn/dotnet/csharp/ Microsoft 微软C#文档
零散的资料在每章节末尾给出。
C# (读作“See Sharp”)是一个**面向对象(Object Oriented)**的编程语言,且进一步支持面向组件(Component Oriented)的编程。C# 基于 C 和 C++ 编写,因而存在相似的语法风格,但更接近于java,由微软(Microsoft)开发。C# 采用统一的类型系统,所有 C# 类型(包括 int 和 double 等基元类型)均继承自一个根 object 类型。 因此,所有类型共用一组通用运算,任何类型的值都可以一致地进行存储、传输和处理。 此外,C# 还支持用户定义的引用类型和值类型,从而支持对象动态分配以及轻量级结构的内嵌式存储。
C# 是 .Net 框架(.Net Framework)的一部分。使用相应的编译器编译C#编写的代码时,无法获得机器代码, 而是获得中间语言( IL)代码。托管代码(Managed Code)是一种中间语言,是执行过程交由运行时管理的代码,在这种情况下,相关的运行时称为公共语言运行时 (CLR)。CLR 负责提取托管代码、将其编译成机器代码,然后执行它。运行时提供多个重要服务,例如自动内存管理、安全边界、类型安全,等等。
一个 C# 程序主要包括以下部分:
using System;
namespace HelloWorldApplication
{
class HelloWorld
{
static void Main(string[] args)
{
/* 我的第一个 C# 程序*/
Console.WriteLine("Hello World");
Console.ReadKey();
}
}
}
在面向对象的程序设计方法中,对象是对客观事物的抽象,而类是对对象的抽象,为所有的对象定义了抽象的属性与行为;反过来说,类是对象是类的一个具体。类定义完毕后不能直接操作,这是因为类仍然是抽象的概念,需要通过实例化来创建类的具体对象,该具体对象被称之为实例。
实际上以电气工程师视角带入进行理解会容易的多,工业现场早已存在许许多多的工艺对象,例如对驱动器对象(drive object),我们将其抽象为轴类(Axis Class),由于运动控制对象通常为多轴机构,因而将轴类实例化为X轴(Axis_X)、Y轴(Axis_Y)、Z轴(Axis_Z)…
在这里我实际上是在复述西门子PLC的组态轴操作,实际上工业上的面向对象技术已经十分成熟。
这里给出一个创建类并实例化的模板:
<access specifier> class class_name//定义类
{
<access specifier> <data type> variable1;//定义成员变量
...
<access specifier> <return type> method1(parameter_list)//定义方法
{
// method body
}
...
}
<access specifier> class main_class_name //带main方法的类
{
static void Main(string[] args)//main方法
{
class_name instance_name = new class_name();//类的实例化(创建对象):类 实例名 = new 类();
instance_name.method1(parameter_list);//方法调用:实例名.方法名();
}
字段(Field)和 属性(Property) 都属于类的成员,是C#面向对象模式中的两个概念。
访问器分为get访问器和set访问器,其语法格式见下:
private code //字段
public string Code //属性
{
get{return code;}
set{code = value;}
}
在.NET3.0及其后续版本中,可简写为自动属性,其语法见下:
private code {get;set};
方法类似C语言的函数,由局部变量和方法主体组成。一个方法是把一些相关的语句组织在一起,用来执行一个任务的语句块。每一个 C# 程序至少有一个带有 Main 方法的类。Main 方法是每个 C# 应用程序的入口点,并在启动程序时由公共语言运行时 (CLR) 调用。
要使用一个方法,需要定义方法并调用方法。
方法的定义模板见下:
<Access Specifier> <Return Type> <Method Name>(Parameter List)//访问修饰符;返回类型;方法名称;参数列表
{
//局部变量
Method Body//方法主体
}
通过指定访问修饰符(如 public 或 private)、可选修饰符(如 abstract 或 sealed)、返回值、方法的名称以及任何方法参数,在类、结构或接口中声明方法。 这些部件一起构成方法签名。注意,出于方法重载的目的,方法的返回类型不是方法签名的一部分。 但是在确定委托和它所指向的方法之间的兼容性时,它是方法签名的一部分。
当调用带有参数的方法时,可按值传递或按引用传递方法的参数。
按值传递参数:会为每个值参数创建一个新的存储位置。
按引用传递参数:与提供给方法的实际参数具有相同的内存位置;需使用ref关键字。
按引用传递参数的案例程序见下:
public void swap(ref int x, ref int y)
{
Method Body//方法主体
}
每当创建类或结构时,将会调用其构造函数。构造函数是一种方法,其名称与其类型的名称相同,没有任何返回类型。构造函数主要用来在创建对象时初始化对象, 即为对象成员变量赋初始值,总与new运算符一起使用在创建对象的语句中。
构造函数默认存在,若未自定义构造函数,则系统调用默认构造函数;否则系统调用自定义构造函数。
默认的构造函数没有任何参数。带有参数的构造函数叫做参数化构造函数,可以在创建对象的同时给对象赋初始值。
class Chinar
{
//默认构造函数
public Chinar() //声明构造函数,函数名与类名相同
{
Method Body //构造函数主体
}
//参数化构造函数
public Chinar(int a, int b, string c, object d)
{
Method Body //构造函数主体
}
}
类分为抽象类和具体类。
抽象类是特殊的类,其用途是提供一个可供多个派生类共享的通用基类定义。抽象类同具体类的核心区别在于不能被实例化,抽象类必须被继承,才能被使用。
通过在类定义前面放置关键字 abstract,可以将类声明为抽象类。
在抽象类中可以定义抽象方法和抽象属性。
public abstract class A //抽象类
{
public abstract string Name { get; } //抽象属性
public abstract void DoWork(int i); //抽象方法
}
注意抽象类也可以像具体类那样定义具体方法和具体属性。
注意抽象类和接口的区别,二者为配合而非替代关系。
抽象类除无法实例化外,其余功能同具体类一致。一个抽象类可以实现多个接口。
静态类与非静态类基本相同,其差异在于静态类无法被实例化,由于不存在任何实例变量,因此可以使用类名本身访问静态类的成员。静态类不能被继承或继承自任何类。静态类需要使用静态修饰符static声明。
类的成员分为静态成员和实例成员,静态成员包括方法、字段、属性或事件。 静态成员属于类,而实例成员则属于对象(类实例)。对于静态成员,无论创建多少个类实例,永远只有一个静态副本。静态成员需要使用静态修饰符static声明。
非静态类可以包含静态成员,静态类只包含静态成员。
面向对象的三个特征:封装、继承、多态。
封装(encapsulation)就是隐藏对象的属性和实现细节,通过 访问修饰符(Access Specifier) 来实现。
访问修饰符定义了类成员的范围和可见性。C# 支持的访问修饰符如下所示:
封装演示代码见下:
public double length;
C#只支持单一继承。
继承(extend)是指当创建一个类时,程序员不需要完全重新编写新的数据成员和成员函数,只需要设计一个新的类,继承了已有的类的成员,从而在无需重新编写原来的类的情况下对这些功能进行扩展。这个已有的类被称为基类,这个新的类被称为派生类(或子类)。子类继承父类的特征和行为,使得子类对象(实例)具有父类的实例域和方法。继承的思想实现了**属于(IS-A)**关系。
class <派生类> : <基类>
class Rectangle: Shape
继承仅适用于类和接口,其他各种类型(结构、委托和枚举)均不支持继承。
通过在类定义前面放置关键字 sealed,可以将类声明为密封类。当一个类被声明为 sealed 时,它不能被继承。
多态(Polymorphism)是指同一个接口,使用不同的实例而执行不同操作。在 C# 中,每个类型都是多态的,因为包括用户定义类型在内的所有类型都继承自 Object。
继承是子类可以调父类的方法,多态是父类可以调子类的方法。
重载(overload):在同一个作用域(一般指一个类)的两个或多个方法函数名相同,参数列表不同的方法叫做重载。
重写(overwrite):子类中为满足自己的需要来重复定义某个方法的不同实现,需要用 override 关键字,被重写的方法必须是虚方法,用的是 virtual 关键字。
**接口(interface)**是一种引用类型,定义了派生类应遵循的标准结构。
接口成员可以包含实例方法、属性、事件、索引器或这四种成员类型的任意组合。接口只包含成员声明而没有具体实现的代码(接口无法直接进行实例化),只在继承该接口的派生类里实现接口成员的定义。
接口使得实现接口的类或结构在形式上保持一致。
定义接口:
interface IMyInterface//通常接口命令以大写 I 字母开头
{
void MethodToImplement();//接口成员,此处声明了方法
}
实现接口:
class InterfaceImplementer : IMyInterface//父类名在前,接口名在后,只能继承一个父类,可以实现多个
{
...
}
C#类型分为值类型(Value types)和引用类型(Reference types)。 值类型的变量直接包含数据;引用类型的变量包含对类型实例的引用,即指向一个内存地址。
C#引用类型和值类型的适用场合和区别
可空类型(Nullable)是一个特殊的数据类型,表示其基础值类型除了正常范围内的值还有一个 null 值,该基础值类型本身不能是可为空的值类型。在处理数据库和其他包含可能未赋值的元素的数据类型时,将 null 赋值给数值类型或布尔型的功能特别有用。
单问号 “?”用于对 int,double,bool 等无法直接赋值为 null 的数据类型进行 null 的赋值。
< data_type> ? <variable_name> = null;
int? i = null //例子
c#的数组初始化风格同c语言不同,数组是一个引用类型,所以需要使用 new 关键字来创建数组的实例:
double[] balance = new double[10]; //声明
balance[0] = 4500.0;//赋值
//或
double[] balance = { 2340.0, 4523.69, 3421.0};
//也可以直接把声明和赋值连在一起
double[] balance = new double[10] { 99, 98, 92, 97, 95};//数组大小可省
尽管字符串为引用类型,基于其特性并不需要new,因此方法上更接近于值类型。
常用的几种方法:
string fname = "Rowan";
等同于
string fname = @"Rowan"; //用 @ 符号加在字符串前面表示其中的转义字符“不”被处理。
也可以使用字符数组来表示字符串。
char[] letters = { 'H', 'e', 'l', 'l','o' };
两个值类型变量的赋值:
值类型的变量直接包含数据,在赋值的时候只是把数据复制并赋给另一个变量。
int var1=2;
int var2=var1; 值类型变量var1变量值2直接复制给var2,var值为2
两个引用类型变量的赋值:
引用类型的变量指向一个内存地址,在赋值的时候把该内存地址赋给另一个变量,因此2个引用变量中保存的是同一引用。
MyClass objectB=objectA; //引用变量的赋值
objectA.val=10; //给objectA.val赋值=10 由于objectB和objectA指向同一内存地址,所以ojbectB.val的值也为10
objectB.val=20; //给objectB.val赋值=20 由于objectB和objectA指向同一内存地址,所以objectA.val的值也为20
C#值类型赋值与引用类型的赋值
C#是一门强类型语言。C#支持两种类型转换方式。
(1)隐式类型转换
C# 默认的以安全方式进行的转换, 不会导致数据丢失。
(2)显式类型转换
即强制类型转换,会造成数据丢失。强制转换需要强制转换运算符,形式为(T)E,将表达式 E 的结果显式转换为类型 T。
double x = 1234.7;
int a;
// Cast double to int.
a = (int)x;
注意值类型同string类型的转换无法通过强制转换运算符实现。
·值类型转string类型时,使用convert转换,例如:
int number = 18; //值类型
string str = num ber.Tostring(); //转换为string类型
·string类型转值类型时,使用Parse转换,例如:
string strNumber = "18";
int num01 = int.Parse(strNumber);