1. 程序结构
C# 程序由一个或多个文件组成。每个文件均包含零个或多个命名空间。一个命名空间包含类、结构、接口、枚举、委托等类型或其他命名空间。
using System;
Console.WriteLine("Hello world!");
namespace Yourspace{
class YourClass{
}
struct YourStruct{
}
interface IYourInterface{
}
delegate int YourDelegate();
enum YourEnum{
}
namespace YourNestedNamespace{
struct YourStruct{
}
}
}
Main
方法是C#应用程序的入口点。
2. 类型系统
C#是一种强类型语言,在变量声明中指定类型,或者使用var
关键字让编译器推断类型。
float temperature;
var limit = 3;
2.1 内置类型
包括值类型和引用类型
- 值类型:bool, byte, sbyte, char, decimal, double, float, int, uint, nint, nuint, long, ulong, short, ushort
值类型分为两类:struct
和enum
内置的数值类型是结构,具有可访问的字段和方法
byte b = byte.MaxValue;
- 引用类型:object, string, dynamic
2.2 自定义类型
2.3 通用类型系统
- 支持继承原则
- CTS(通用继承原则)中的每种类型被定义为值类型或引用类型
2.4 命名空间
System.Console.WriteLine("Hello World!");
System
是一个命名空间,Console
是该命名空间的一个类
2.5 类简介
定义为class
的类型是引用类型
public class Customer{
}
Customer object1 = new Customer(); //create an object
类继承:
public class Manager:Employee{
}
using System;
public class Person{
public Person(){
Name = 'unknown';
}
public Person(string name){
Name = name;
}
//auto-implemented readonly property
public string Name { get; }
public override string ToString(){
return Name;
}
}
class TestPerson{
static void Main(){
ver person = new Person();
Console.WriteLine(person1.Name);
}
}
2.6 接口
接口包含非抽象class或struct必须实现的一组相关功能的定义。
interface IEquatable<T>{
bool Equals(T obj);
}
接口名称必须是有效的 C# 标识符名称。 按照约定,接口名称以大写字母 I 开头。
接口可以包含实例方法、属性、事件、索引器或这四种成员类型的任意组合。
2.7 泛型
public class GenericList<T>{
public void Add(T input){ }
}
class TestGenericList{
private class ExampleClass{}
static void Main(){
GenericList<int> list1 = new GenericList<int>();
list1.Add(1);
GenericList<string> list2 = new GenericList<string>();
list2.Add("");
GenericList<ExampleClass> list3 = new GenericList<ExampleClass>();
list3.Add(new ExampleClass());
}
}
使用泛型类型可以最大限度的重用代码、保护类型安全性以及提高性能。
2.8 匿名类型
匿名类型可用来将一组只读属性封装到单个对象中,无需首先显示定义一个类型。
var v = new { Amount = 108, Message = 'hello' };
Console.WriteLine(v.Amount + v.Message);
3 面向对象的编程
C#没有全局变量和方法。
3.1 对象
类或结构定义的作用类似于蓝图,指定该类型可以进行哪些操作。
-
结构实例与类实例
由于类是引用类型,因此类对象的变量引用该对象在托管堆上的地址。类的实例是使用new运算符创建的。public class Person{ public string Name { get; set; } public int Age {get; set; } public Person(string name, int age){ Name = name; Age = age; } } class Program{ static void Main(){ Person person1 = new Person("Leopold", 6); Console.WriteLine("person1 Name = {0} Age={1}", person1.Name, person1.Age); } Person person2 = person1; person2.Name = "Molly"; person2.Age = 16; }
如果将同一类型的第二个变量分配给第一个变量,则两个变量都引用该地址的对象。
结构则是值类型,因此结构对象的变量具有整个对象的副本。结构的实例也可以使用new
运算符来创建,但不是必须。public struct Person{ // ... } Person p2 = p1;
p1,p2的内存在线程堆栈上进行分配,该内存随声明它的类型或方法一起回收。
-
对象标识与值相等性
两个变量是否表示内存中的同一对象,还是想知道这两个对象的一个或多个字段的值是否相等。
确定两个类实例是否引用内存的同一位置:
if(p2.Equals(p1)){
Console.WriteLine("p2 and p1 have the same values");
}
3.2 继承
成员被继承的类称为“基类”,继承这些成员的类称为“派生类”。
- 抽象方法和虚方法
基类将方法声明为virtual
时,派生类可以使用其自己的实现override
该方法。
3.3 多形性
public class Shape{
public int X {get; private set;}
public int Y {get; private set;}
public int Height {get; set;}
public int Width {get; set;}
public virtual void Draw(){
Console.WriteLine("Performing base class drawing tasks");
}
}
public class Circle: Shape{
public override void Draw(){
Console.WriteLine("Drawing a circle");
base.Draw();
}
}
public class Rectangle: Shape{
public override void Draw(){
Console.WriteLine("Drawing a rectangle");
base.Draw();
}
}
public class Triangle: Shape{
public override void Draw(){
//
}
}
当派生类从基类继承时,它包括基类的所有成员。基类中声明的所有行为都是派生类的一部分。这使派生类的对象能够被视为基类的对象。
- 派生类可以重写基类中的虚拟成员,并定义新行为
- 派生类可能会继承最接近的基类方法而不重写方法
4 功能技术
4.1 模式匹配
int? maybe = 12;
if(maybe is int number){
//
}else{
//
}
上述代码使声明模式,用于测试变量类型并将其分配给新变量。
string message = "This is not the null string";
if(message is not null){
//...
}
4.3 析构元组和其他类型
var (name, address, city, zip) = contact.GetAddressInfo();
(string ciy, int poputation, double area) = QueryCityData("New York City");
var (_,_,_, pol1,_,pol2) = QueryCityData("New York City");
5 异常和错误
在许多情况下,异常并不是由代码直接调用的方法抛出,而是由调用堆栈中再往下的另一方法抛出。
5.1 使用异常
class CustomException: Exception{
public CustomException(string message){
}
}
private static void TestThrow(){
throw new CustomException("Custom exception in TestThrow()");
}
try{
TestThrow();
}
catch (CustomException ex){
System.Console.WriteLine(ex.ToString());
}
5.2 异常处理
finally
块让你可以清理再try
块中所执行的操作。无论是否会引发异常或者找到匹配异常类型的catch
块,finally
块都将始终运行。
FileStream? file = null;
FileInfo fileinfo = new System.IO.FileInfo("./file.txt");
try{
file = fileInfo.OpenWrite();
file.WriteByte(0xF);
}
finally{
//Check for null because OpenWrite might have failed
file?.Close();
}
5.3 创建和引发异常
异常用于指示在运行程序时发生了错误。此时将创建一个描述错误的异常对象,然后使用throw关键字引发。
public class InvalidDepartmentException:Exception{
public InvalidDepartmentException():base(){}
public InvalidDepartmentException(string message):base(message){}
public InvalidDepartmentException(string message,Exception inner):base(message, inner){}
}