C# 知识结构
对于一个工作多年的程序员而言,接口、反射、索引器、事件、委托这些耳熟能详的词汇,提起来别说多简略了,然而让老司机坐在那一个人拿起一支笔,把脑海中对 C# 知识结构进行梳理一下,大抵是写不了多内容的,起因是什么呢,是忘记? 当然不是,每天面对代码的老司机当然不会忘记。
基本的起因是常识没有网格化。
知识结构网格化,对于当初频繁变动的技术格局来说,是势在必行且能够安身立命的基本
这个办法不仅应用编程菜鸟,同样应用编程老司机。
接下来咱们进入正题,先来看一副图
原图,可通过自己的 Github 下载,连贯如下
https://github.com/yuyue5945/…
在导图上列出如下几个根底的问题,这几个根底问题,会始终随同着咱们的编程生涯
什么是类?
什么是对象?对于类与对象的关系
类和接口之间的关系
什么是封装,继承,多态
接下来,咱们就走进 C# 根底构造的世界,从新梳理一下咱们的知识结构吧
- C# 知识结构
-
根底概念
-
类 - 继承、封装、多态
- 封装
-
继承
- 类定义关键字
- 类调用关键字
- 动静
-
接口(Interface)
- 定义接口: MyInterface.cs
- 接下来咱们来实现以上接口:InterfaceImplementer.cs
-
命名空间(Namespace)
- 定义命名空间
- using 关键字
-
-
高级利用
-
个性(Attribute)
- 规定个性(Attribute)
- 预约义个性(Attribute)
-
反射(Reflection)
- 反射(Reflection)的用处
-
属性(Property)
- 拜访器(Accessors)
-
索引器(Indexer)
- 索引器(Indexer)的用处
-
委托(Delegate)
- 申明委托(Delegate)
- 实例化委托(Delegate)
-
事件(Event)
- 通过事件应用委托
- 汇合(Collection)
-
泛型(Generic)
- 泛型(Generic)的个性
-
匿名办法
- 编写匿名办法的语法
-
托管与非托管
- 指针变量
-
多线程
- 线程生命周期
- 主线程
- 博主 GitHub 地址
- 关注公众号不迷路
-
根底概念
类 - 继承、封装、多态
在 C# 语言中创立的任何我的项目都有类的存在,通过类能很好地体现面向对象语言中封装、继承、多态的个性。本节将解说 C# 中的类和定义类形式。
在后面的学习中曾经屡次应用过类,类定义的语法模式并不简单,请记住 class 关键字,它是定义类的关键字。
类定义的具体语法模式如下。
类的拜访修饰符 修饰符 类名
{
类的成员
}
其中:
类的拜访修饰符:用于设定对类的拜访限度,包含 public、internal 或者不写,用 internal 或者不写时代表只能在以后我的项目中拜访类;public 则代表能够在任何我的项目中拜访类。
修饰符:修饰符是对类自身特点的形容,包含 abstract、sealed 和 static。abstract 是形象的意思,应用它修饰符的类不能被实例化;sealed 润饰的类是密封类,不能 被继承;static 润饰的类是动态类,不能被实例化。
类名:类名用于形容类的性能,因而在定义类名时最好是具备实际意义,这样不便用户了解类中形容的内容。在同一个命名空间下类名必须是惟一的。
类的成员:在类中能定义的元素,次要包含字段、属性、办法。
封装
封装波及到类定义、类申明、类成员
类成员包含字段、属性、办法、事件。
函数成员包含结构器、析构器、属性、办法体、办法头等
继承
继承是面向对象程序设计中最重要的概念之一。继承容许咱们依据一个类来定义另一个类,这使得创立和保护应用程序变得更容易。同时也有利于重用代码和节俭开发工夫。
当创立一个类时,程序员不须要齐全从新编写新的数据成员和成员函数,只须要设计一个新的类,继承了已有的类的成员即可。这个已有的类被称为的基类,这个新的类被称为派生类。
继承的思维实现了 属于(IS-A)关系。例如,哺乳动物 属于(IS-A)动物,狗 属于(IS-A)哺乳动物,因而狗 属于(IS-A)动物。
基类和派生类
一个类能够派生自多个类或接口,这意味着它能够从多个基类或接口继承数据和函数。
类定义关键字
继承讲的是基类和派生类,存在单继承、多重继承两种状况
基类在派生类初始化之前主动进行初始化
构造函数和析构函数不能被继承
若类被标注了 abstract 则强制派生类笼罩基类的办法,目标为了对外提供对立的办法签名。
若类被标注了 sealed 则阐明该类伪密封类,不可被继承
若类被标注了 partial 则阐明该类被宰割在几个文件中
类调用关键字
Base 示意调用基类办法、或者被重写的办法
New 示意重写同名法法、暗藏父类办法
Override 示意 重载父类办法
还存在组合办法的模式,不过不举荐
override–virtual
new–virtual
C# 中创立派生类的语法如下:
< 拜访修饰符符 > class < 基类 >
{...}
class < 派生类 > : < 基类 >
{...}
假如,有一个基类 Shape,它的派生类是 Rectangle:实例
using System;
namespace InheritanceApplication
{
class Shape
{public void setWidth(int w)
{width = w;}
public void setHeight(int h)
{height = h;}
protected int width;
protected int height;
}
// 派生类
class Rectangle: Shape
{public int getArea()
{return (width * height);
}
}
class RectangleTester
{static void Main(string[] args)
{Rectangle Rect = new Rectangle();
Rect.setWidth(5);
Rect.setHeight(7);
// 打印对象的面积
Console.WriteLine("总面积:{0}", Rect.getArea());
Console.ReadKey();}
}
}
当下面的代码被编译和执行时,它会产生下列后果:
总面积:35
动静
办法重载就是一种多态
接口(Interface)
接口定义了所有类继承接口时应遵循的语法合同。接口定义了语法合同 “ 是什么 ” 局部,派生类定义了语法合同 “ 怎么做 ” 局部。
接口定义了属性、办法和事件,这些都是接口的成员。接口只蕴含了成员的申明。成员的定义是派生类的责任。接口提供了派生类应遵循的规范构造。
接口使得实现接口的类或构造在模式上保持一致。
抽象类在某种程度上与接口相似,然而,它们大多只是用在当只有多数办法由基类申明由派生类实现时。
定义接口: MyInterface.cs
接口应用 interface 关键字申明,它与类的申明相似。接口申明默认是 public 的。上面是一个接口申明的实例:
interface IMyInterface
{void MethodToImplement();
}
以上代码定义了接口 IMyInterface。通常接口命令以 I 字母结尾,这个接口只有一个办法 MethodToImplement(),没有参数和返回值,当然咱们能够依照需要设置参数和返回值。
值得注意的是,该办法并没有具体的实现。
接下来咱们来实现以上接口:InterfaceImplementer.cs
实例
using System;
interface IMyInterface
{
// 接口成员
void MethodToImplement();}
class InterfaceImplementer : IMyInterface
{static void Main()
{InterfaceImplementer iImp = new InterfaceImplementer();
iImp.MethodToImplement();}
public void MethodToImplement()
{Console.WriteLine("MethodToImplement() called.");
}
}
InterfaceImplementer 类实现了 IMyInterface 接口,接口的实现与类的继承语法格局相似:
class InterfaceImplementer : IMyInterface
继承接口后,咱们须要实现接口的办法 MethodToImplement() , 办法名必须与接口定义的办法名统一。
命名空间(Namespace)
命名空间的设计目标是提供一种让一组名称与其余名称分隔开的形式。在一个命名空间中申明的类的名称与另一个命名空间中申明的雷同的类的名称不抵触。
咱们举一个计算机系统中的例子,一个文件夹 (目录) 中能够蕴含多个文件夹,每个文件夹中不能有雷同的文件名,但不同文件夹中的文件能够重名。
定义命名空间
命名空间的定义是以关键字 namespace 开始,后跟命名空间的名称,如下所示:
namespace namespace_name
{// 代码申明}
为了调用反对命名空间版本的函数或变量,会把命名空间的名称置于后面,如下所示:namespace_name.item_name;
上面的程序演示了命名空间的用法:实例
using System;
namespace first_space
{
class namespace_cl
{public void func()
{Console.WriteLine("Inside first_space");
}
}
}
namespace second_space
{
class namespace_cl
{public void func()
{Console.WriteLine("Inside second_space");
}
}
}
class TestClass
{static void Main(string[] args)
{first_space.namespace_cl fc = new first_space.namespace_cl();
second_space.namespace_cl sc = new second_space.namespace_cl();
fc.func();
sc.func();
Console.ReadKey();}
}
当下面的代码被编译和执行时,它会产生下列后果:
Inside first_space
Inside second_space
using 关键字
using 关键字表明程序应用的是给定命名空间中的名称。例如,咱们在程序中应用 System 命名空间,其中定义了类 Console。咱们能够只写:
Console.WriteLine ("Hello there");
咱们能够写齐全限定名称,如下:
System.Console.WriteLine("Hello there");
高级利用
个性(Attribute)
个性(Attribute)是用于在运行时传递程序中各种元素(比方类、办法、构造、枚举、组件等)的行为信息的申明性标签。您能够通过应用个性向程序增加申明性信息。一个申明性标签是通过搁置在它所利用的元素后面的方括号([])来形容的。
个性(Attribute)用于增加元数据,如编译器指令和正文、形容、办法、类等其余信息。.Net 框架提供了两种类型的个性:预约义个性和自定义个性。
规定个性(Attribute)
规定个性(Attribute)的语法如下:
[attribute(positional_parameters, name_parameter = value, ...)]
element
个性(Attribute)的名称和值是在方括号内规定的,搁置在它所利用的元素之前。positional_parameters 规定必须的信息,name_parameter 规定可选的信息。
预约义个性(Attribute)
.Net 框架提供了三种预约义个性:
- AttributeUsage
- Conditional
- Obsolete
反射(Reflection)
反射指程序能够拜访、检测和批改它自身状态或行为的一种能力。
程序集蕴含模块,而模块蕴含类型,类型又蕴含成员。反射则提供了封装程序集、模块和类型的对象。
您能够应用反射动静地创立类型的实例,将类型绑定到现有对象,或从现有对象中获取类型。而后,能够调用类型的办法或拜访其字段和属性。
优缺点
长处:
1、反射进步了程序的灵活性和扩展性。
2、升高耦合性,进步自适应能力。
3、它容许程序创立和管制任何类的对象,无需提前硬编码指标类。
毛病:
1、性能问题:应用反射基本上是一种解释操作,用于字段和办法接入时要远慢于间接代码。因而反射机制次要利用在对灵活性和拓展性要求很高的零碎框架上,一般程序不倡议应用。
2、应用反射会含糊程序外部逻辑;程序员心愿在源代码中看到程序的逻辑,反射却绕过了源代码的技术,因此会带来保护的问题,反射代码比相应的间接代码更简单。
反射(Reflection)的用处
反射(Reflection)有下列用处:
- 它容许在运行时查看个性(attribute)信息。
- 它容许审查汇合中的各种类型,以及实例化这些类型。
- 它容许提早绑定的办法和属性(property)。
- 它容许在运行时创立新类型,而后应用这些类型执行一些工作。
属性(Property)
属性(Property)是类(class)、构造(structure)和接口(interface)的命名(named)成员。类或构造中的成员变量或办法称为 域(Field)。属性(Property)是域(Field)的扩大,且可应用雷同的语法来拜访。它们应用 拜访器(accessors)让公有域的值可被读写或操作。
属性(Property)不会确定存储地位。相同,它们具备可读写或计算它们值的 拜访器(accessors)。
例如,有一个名为 Student 的类,带有 age、name 和 code 的公有域。咱们不能在类的范畴以外间接拜访这些域,然而咱们能够领有拜访这些公有域的属性。
拜访器(Accessors)
属性(Property)的拜访器(accessor)蕴含有助于获取(读取或计算)或设置(写入)属性的可执行语句。拜访器(accessor)申明可蕴含一个 get 拜访器、一个 set 拜访器,或者同时蕴含二者。例如:
// 申明类型为 string 的 Code 属性
public string Code
{
get
{return code;}
set
{code = value;}
}
// 申明类型为 string 的 Name 属性
public string Name
{
get
{return name;}
set
{name = value;}
}
// 申明类型为 int 的 Age 属性
public int Age
{
get
{return age;}
set
{age = value;}
}
索引器(Indexer)
索引器(Indexer)容许一个对象能够像数组一样应用下标的形式来拜访。
当您为类定义一个索引器时,该类的行为就会像一个 虚构数组(virtual array)一样。您能够应用数组拜访运算符 [] 来拜访该类的的成员。
语法
一维索引器的语法如下:
element-type this[int index]
{
// get 拜访器
get
{// 返回 index 指定的值}
// set 拜访器
set
{// 设置 index 指定的值}
}
索引器(Indexer)的用处
索引器的行为的申明在某种程度上相似于属性(property)。就像属性(property),您可应用 get 和 set 拜访器来定义索引器。然而,属性返回或设置一个特定的数据成员,而索引器返回或设置对象实例的一个特定值。换句话说,它把实例数据分为更小的局部,并索引每个局部,获取或设置每个局部。
定义一个属性(property)包含提供属性名称。索引器定义的时候不带有名称,但带有 this 关键字,它指向对象实例。
委托(Delegate)
C# 中的委托(Delegate)相似于 C 或 C++ 中函数的指针。委托(Delegate)是存有对某个办法的援用的一种援用类型变量。援用可在运行时被扭转。
委托(Delegate)特地用于实现事件和回调办法。所有的委托(Delegate)都派生自 System.Delegate 类。
申明委托(Delegate)
委托申明决定了可由该委托援用的办法。委托可指向一个与其具备雷同标签的办法。
例如,假如有一个委托:
public delegate int MyDelegate (string s);
下面的委托可被用于援用任何一个带有一个繁多的 string 参数的办法,并返回一个 int 类型变量。
申明委托的语法如下:
delegate <return type> <delegate-name> <parameter list>
实例化委托(Delegate)
一旦申明了委托类型,委托对象必须应用 new 关键字来创立,且与一个特定的办法无关。当创立委托时,传递到 new 语句的参数就像办法调用一样书写,然而不带有参数。例如:
public delegate void printString(string s);
...
printString ps1 = new printString(WriteToScreen);
printString ps2 = new printString(WriteToFile);
事件(Event)
事件(Event)基本上说是一个用户操作,如按键、点击、鼠标挪动等等,或者是一些提示信息,如系统生成的告诉。应用程序须要在事件产生时响应事件。例如,中断。
C# 中应用事件机制实现线程间的通信。
通过事件应用委托
事件在类中申明且生成,且通过应用同一个类或其余类中的委托与事件处理程序关联。蕴含事件的类用于公布事件。这被称为 公布器(publisher)类。其余承受该事件的类被称为 订阅器(subscriber)类。事件应用 公布 - 订阅(publisher-subscriber)模型。
公布器(publisher)是一个蕴含事件和委托定义的对象。事件和委托之间的分割也定义在这个对象中。公布器(publisher)类的对象调用这个事件,并告诉其余的对象。
订阅器(subscriber)是一个承受事件并提供事件处理程序的对象。在公布器(publisher)类中的委托调用订阅器(subscriber)类中的办法(事件处理程序)。
申明事件(Event)
在类的外部申明事件,首先必须申明该事件的委托类型。例如:
public delegate void BoilerLogHandler(string status);
而后,申明事件自身,应用 event 关键字:// 基于下面的委托定义事件
public event BoilerLogHandler BoilerEventLog;
汇合(Collection)
汇合(Collection)类是专门用于数据存储和检索的类。这些类提供了对栈(stack)、队列(queue)、列表(list)和哈希表(hash table)的反对。大多数汇合类实现了雷同的接口。
汇合(Collection)类服务于不同的目标,如为元素动静分配内存,基于索引拜访列表项等等。这些类创立 Object 类的对象的汇合。在 C# 中,Object 类是所有数据类型的基类。
泛型(Generic)
泛型(Generic)容许您提早编写类或办法中的编程元素的数据类型的标准,直到理论在程序中应用它的时候。换句话说,泛型容许您编写一个能够与任何数据类型一起工作的类或办法。
您能够通过数据类型的代替参数编写类或办法的标准。当编译器遇到类的构造函数或办法的函数调用时,它会生成代码来解决指定的数据类型。
泛型(Generic)的个性
应用泛型是一种加强程序性能的技术,具体表现在以下几个方面:
- 它有助于您最大限度地重用代码、爱护类型的平安以及进步性能。
- 您能够创立泛型汇合类。.NET 框架类库在 System.Collections.Generic 命名空间中蕴含了一些新的泛型汇合类。您能够应用这些泛型汇合类来代替 System.Collections 中的汇合类。
- 您能够创立本人的泛型接口、泛型类、泛型办法、泛型事件和泛型委托。
- 您能够对泛型类进行束缚以拜访特定数据类型的办法。
- 对于泛型数据类型中应用的类型的信息可在运行时通过应用反射获取。
匿名办法
咱们曾经提到过,委托是用于援用与其具备雷同标签的办法。换句话说,您能够应用委托对象调用可由委托援用的办法。
匿名办法(Anonymous methods)提供了一种传递代码块作为委托参数的技术。匿名办法是没有名称只有主体的办法。
在匿名办法中您不须要指定返回类型,它是从办法主体内的 return 语句推断的。
编写匿名办法的语法
匿名办法是通过应用 delegate 关键字创立委托实例来申明的。例如:
delegate void NumberChanger(int n);
...
NumberChanger nc = delegate(int x)
{Console.WriteLine("Anonymous Method: {0}", x);
};
代码块 Console.WriteLine(“Anonymous Method: {0}”, x); 是匿名办法的主体。
委托能够通过匿名办法调用,也能够通过命名办法调用,即,通过向委托对象传递办法参数。
留神: 匿名办法的主体前面须要一个 ;
托管与非托管
当一个代码块应用 unsafe 修饰符标记时,C# 容许在函数中应用指针变量。不平安代码或非托管代码是指应用了指针变量的代码块。
指针变量
指针 是值为另一个变量的地址的变量,即,内存地位的间接地址。就像其余变量或常量,您必须在应用指针存储其余变量地址之前申明指针。
指针变量申明的个别模式为:
type* var-name;
多线程
线程 被定义为程序的执行门路。每个线程都定义了一个独特的控制流。如果您的应用程序波及到简单的和耗时的操作,那么设置不同的线程执行门路往往是无益的,每个线程执行特定的工作。
线程是轻量级过程。一个应用线程的常见实例是古代操作系统中并行编程的实现。应用线程节俭了 CPU 周期的节约,同时进步了应用程序的效率。
到目前为止咱们编写的程序是一个单线程作为应用程序的运行实例的繁多的过程运行的。然而,这样子应用程序同时只能执行一个工作。为了同时执行多个工作,它能够被划分为更小的线程。
线程生命周期
线程生命周期开始于 System.Threading.Thread 类的对象被创立时,完结于线程被终止或实现执行时。
上面列出了线程生命周期中的各种状态:
- 未启动状态:当线程实例被创立但 Start 办法未被调用时的情况。
- 就绪状态:当线程筹备好运行并期待 CPU 周期时的情况。
-
不可运行状态:上面的几种状况下线程是不可运行的:
- 曾经调用 Sleep 办法
- 曾经调用 Wait 办法
- 通过 I/O 操作阻塞
- 死亡状态:当线程已实现执行或已停止时的情况。
主线程
在 C# 中,System.Threading.Thread 类用于线程的工作。它容许创立并拜访多线程应用程序中的单个线程。过程中第一个被执行的线程称为主线程。
当 C# 程序开始执行时,主线程主动创立。应用 Thread 类创立的线程被主线程的子线程调用。您能够应用 Thread 类的 CurrentThread 属性拜访线程。
上面的程序演示了主线程的执行:
实例
using System;
using System.Threading;
namespace MultithreadingApplication
{
class MainThreadProgram
{static void Main(string[] args)
{
Thread th = Thread.CurrentThread;
th.Name = "MainThread";
Console.WriteLine("This is {0}", th.Name);
Console.ReadKey();}
}
}
当下面的代码被编译和执行时,它会产生下列后果:
This is MainThread
## 博主 GitHub 地址
https://github.com/yuyue5945
关注公众号不迷路