切换导航
{{systemName}}
{{ info.Title }}
{{info.Title}}
{{ menu.Title }}
{{menu.Title}}
登录
|
退出
搜索
秘籍列表
ASP.NET基础及企业项目应用
必备工具的安装及C#基础的介绍
ASP.NET高级
数据库操作
Web前端基础
ASP.NET建站核心
ASP.NET MVC
.Net项目
ASP.NET 数据库及各种ORM使用教程
面试
ASP.NET高级
[TOC] [【第二章ASP.NET提高(ASP.NET编程教学)视频】](https://www.bilibili.com/video/BV1uW411o7Uk/?share_source=copy_web&vd_source=160a6bb7aac774f8784a54ccad1255a1 "【第二章ASP.NET提高(ASP.NET编程教学)视频】") ####第一节C#的特性、反射及属性 #####1. 特性(Attribute) 特性(Attribute)是用于在运行时传递程序中各种元素(比如类、方法、结构、枚举、组件等)的行为信息的声明性标签。您可以通过使用特性向程序添加声明性信息。一个声明性标签是通过放置在它所应用的元素前面的方括号[()]来描述的。 特性(Attribute)用于添加元数据,如编译器指令和注释、描述、方法、类等其他信息。.Net 框架提供了两种类型的特性:预定义特性和自定义特性。 (1) 预定义特性(Attribute) .Net 框架提供了三种预定义特性: 1) AttributeUsage 预定义特性AttributeUsage 描述了如何使用一个自定义特性类。它规定了特性可应用到的项目的类型。 ``` [AttributeUsage(AttributeTargets.Class | AttributeTargets.Constructor | AttributeTargets.Field | AttributeTargets.Method | AttributeTargets.Property//参数 validon 规定特性可被放置的语言元素。它是枚举器 AttributeTargets 的值的组合。默认值是 AttributeTargets.All。 , AllowMultiple = true//参数 allowmultiple(可选的)为该特性的 AllowMultiple 属性(property)提供一个布尔值。如果为 true,则该特性是多用的。默认值是 false(单用的)。 , Inherited = true//参数 inherited(可选的)为该特性的 Inherited 属性(property)提供一个布尔值。如果为 true,则该特性可被派生类继承。默认值是 false(不被继承)。 )] ``` 2) Conditional 这个预定义特性标记了一个条件方法,其执行依赖于指定的预处理标识符。 它会引起方法调用的条件编译,取决于指定的值,比如 Debug 或 Trace。例如,当调试代码时显示变量的值。 ``` using System; using System.Collections.Generic; using System.Diagnostics; using System.Linq; using System.Text; using System.Threading.Tasks; namespace Attribute { public class Myclass { [Conditional("DEBUG")] public static void Message(string msg) { Console.WriteLine(msg); } } public class Myclass1 { [Conditional("Trace")] public void Message1(string msg) { Console.WriteLine(msg); } } class Program { static void function1() { Myclass.Message("In Function 1."); function2(); } static void function2() { Myclass.Message("In Function 2."); } static void Main(string[] args) { Myclass.Message("In Main function.");//静态方法可以直接调用 function1(); Myclass1 class1 = new Myclass1(); class1.Message1("111111"); Console.ReadKey(); } } } ``` 结果: ``` In Main function In Function 1 In Function 2 ```  注意:利用 Conditional 属性,我们可以定义条件方法。Conditional属性通过测试条件编译符号来确定适用的条件。当运行到一个条件方法调用时,是否执行该调用,要根据出现该调用时是否已定义了此符号来确定。如果定义了此符号,则执行该调用;否则省略该调用(包括对调用的参数的计算)。使用Conditional是封闭#if和#endif内部方法的替代方法,它更整洁,更别致、减少了出错的机会。 条件方法要受到以下限制: 条件方法必须是类声明或结构声明中的方法。如果在接口声明中的方法上指定Conditional属性,将出现编译时错误。 条件方法必须具有返回类型。 不能用override修饰符标记条件方法。但是,可以用virtual修饰符标记条件方法。此类方法的重写方法隐含为有条件的方法,而且不能用Conditional属性显式标记。 条件方法不能是接口方法的实现。否则将发生编译时错误。 如果条件方法用在"委托创建表达式"中,也会发生编译时错误这里需要注意的是:如果创建一个没有定义任何条件的方法,那么默认只要调用就总是会执行此方法,如果你想通过条件来判断执行,那么该方法上必须至少包含一个conditional特性所定义的条件,它才会响应你定义的条件。 3) Obsolete 这个预定义特性标记了不应被使用的程序实体。它可以让您通知编译器丢弃某个特定的目标元素。例如,当一个新方法被用在一个类中,但是您仍然想要保持类中的旧方法,您可以通过显示一个应该使用新方法,而不是旧方法的消息,来把它标记为 obsolete(过时的)。 ``` [Obsolete("Don't use OldMethod, use NewMethod instead"//参数 message,是一个字符串,描述项目为什么过时的原因以及该替代使用什么。 , false//参数 iserror,是一个布尔值。如果该值为 true,编译器应把该项目的使用当作一个错误。默认值是 false(编译器生成一个警告)。 )] ``` 效果如图:  (2) 创建自定义特性(Attribute) .Net 框架允许创建自定义特性,用于存储声明性的信息,且可在运行时被检索。该信息根据设计标准和应用程序需要,可与任何目标元素相关。 创建并使用自定义特性包含四个步骤:声明自定义特性、构建自定义特性、在目标程序元素上应用自定义特性、通过反射访问特性,最后一个步骤包含编写一个简单的程序来读取元数据以便查找各种符号。元数据是用于描述其他数据的数据和信息。该程序应使用反射来在运行时访问特性。我们将在下一部分详细讨论这点。 1)声明自定义特性并构建自定义特性 让我们构建一个名为 DeBugInfo 的自定义特性,该特性将存储调试程序获得的信息。它存储下面的信息:bug 的代码编号、辨认该 bug 的开发人员名字、最后一次审查该代码的日期、一个存储了开发人员标记的字符串消息。 我们的 DeBugInfo 类将带有三个用于存储前三个信息的私有属性(property)和一个用于存储消息的公有属性(property)。所以 bug 编号、开发人员名字和审查日期将是 DeBugInfo 类的必需的定位( positional)参数,消息将是一个可选的命名(named)参数。 每个特性必须至少有一个构造函数。必需的定位(positional参数应通过构造函数传递。下面的代码演示了 DeBugInfo 类:一个新的自定义特性应派生自 System.Attribute 类。 ``` [AttributeUsage(AttributeTargets.Class | AttributeTargets.Constructor | AttributeTargets.Field | AttributeTargets.Method | AttributeTargets.Property, AllowMultiple = true)] public class AttrInfo : System.Attribute { private int bugNo; private string developer; private string lastReview; public string message; public AttrInfo(int bg, string dev, string d) { this.bugNo = bg; this.developer = dev; this.lastReview = d; } public int BugNo { get { return bugNo; } } public string Developer { get { return developer; } } public string LastReview { get { return lastReview; } } public string Message { get { return message; } set { message = value; } } } ``` 2)应用自定义特性 通过把特性放置在紧接着它的目标之前,来应用该特性: ``` [DeBugInfo(45, "Zara Ali", "12/8/2012", Message = "Return type mismatch")] [DeBugInfo(49, "Nuha Ali", "10/10/2012", Message = "Unused variable")] class Rectangle { // 成员变量 protected double length; protected double width; public Rectangle(double l, double w) { length = l; width = w; } [DeBugInfo(55, "Zara Ali", "19/10/2012", Message = "Return type mismatch")] public double GetArea() { return length * width; } [DeBugInfo(56, "Zara Ali", "19/10/2012")] public void Display() { Console.WriteLine("Length: {0}", length); Console.WriteLine("Width: {0}", width); Console.WriteLine("Area: {0}", GetArea()); } } ``` 在下一块中,我们将使用 Reflection 类对象来检索这些信息。 #####2. C# 反射(Reflection) 反射指程序可以访问、检测和修改它本身状态或行为的一种能力。 程序集包含模块,而模块包含类型,类型又包含成员。反射则提供了封装程序集、模块和类型的对象。 您可以使用反射动态地创建类型的实例,将类型绑定到现有对象,或从现有对象中获取类型。然后,可以调用类型的方法或访问其字段和属性。 (1)优缺点 1)优点: 1、反射提高了程序的灵活性和扩展性。 2、降低耦合性,提高自适应能力。 3、它允许程序创建和控制任何类的对象,无需提前硬编码目标类。 2)缺点: 1、性能问题:使用反射基本上是一种解释操作,用于字段和方法接入时要远慢于直接代码。因此反射机制主要应用在对灵活性和拓展性要求很高的系统框架上,普通程序不建议使用。 2、使用反射会模糊程序内部逻辑;程序员希望在源代码中看到程序的逻辑,反射却绕过了源代码的技术,因而会带来维护的问题,反射代码比相应的直接代码更复杂。 (2)反射(Reflection)的用途 1、它允许在运行时查看特性(attribute)信息。 2、它允许审查集合中的各种类型,以及实例化这些类型。 3、它允许延迟绑定的方法和属性(property)。 4、它允许在运行时创建新类型,然后使用这些类型执行一些任务。 (3)查看元数据 我们已经在上面的章节中提到过,使用反射(Reflection)可以查看特性(attribute)信息。 System.Reflection 类的 MemberInfo 对象需要被初始化,用于发现与类相关的特性(attribute)。为了做到这点,您可以定义目标类的一个对象,如下: ``` System.Reflection.MemberInfo info = typeof(MyClass); using System; using System.Reflection; namespace AutoReflection { [AttributeUsage(AttributeTargets.Class | AttributeTargets.Constructor | AttributeTargets.Field | AttributeTargets.Method | AttributeTargets.Property, AllowMultiple = true)] public class DeBugInfo : System.Attribute { private int bugNo; private string developer; private string lastReview; public string message; public DeBugInfo(int bg, string dev, string d) { this.bugNo = bg; this.developer = dev; this.lastReview = d; } public int BugNo { get { return bugNo; } } public string Developer { get { return developer; } } public string LastReview { get { return lastReview; } } public string Message { get { return message; } set { message = value; } } } [DeBugInfo(45, "Zara Ali", "12/8/2012", Message = "Return type mismatch")] [DeBugInfo(49, "Nuha Ali", "10/10/2012", Message = "Unused variable")] class Rectangle { // 成员变量 protected double length; protected double width; public Rectangle(double l, double w) { length = l; width = w; } [DeBugInfo(55, "Zara Ali", "19/10/2012", Message = "Return type mismatch")] public double GetArea() { return length * width; } [DeBugInfo(56, "Zara Ali", "19/10/2012", Message = "Return Three")] public void Display() { Console.WriteLine("Length: {0}", length); Console.WriteLine("Width: {0}", width); Console.WriteLine("Area: {0}", GetArea()); } }//end class Rectangle class Program { static void Main(string[] args) { Rectangle r = new Rectangle(4.5, 7.5); r.Display(); Type type = typeof(Rectangle); // 遍历 Rectangle 类的特性 foreach (Object attributes in type.GetCustomAttributes(false)) { DeBugInfo dbi = (DeBugInfo)attributes; if (null != dbi) { Console.WriteLine("Bug no: {0}", dbi.BugNo); Console.WriteLine("Developer: {0}", dbi.Developer); Console.WriteLine("Last Reviewed: {0}", dbi.LastReview); Console.WriteLine("Remarks: {0}", dbi.Message); } } // 遍历方法特性 foreach (MethodInfo m in type.GetMethods()) { object[] attrs = m.GetCustomAttributes(true); //反射获得用户自定义属性数组 foreach (System.Attribute attr in attrs) { if (attr is DeBugInfo) { DeBugInfo dbi = (DeBugInfo)attr; Console.WriteLine("Bug no: {0}, for Method: {1}", dbi.BugNo, m.Name); Console.WriteLine("Developer: {0}", dbi.Developer); Console.WriteLine("Last Reviewed: {0}", dbi.LastReview); Console.WriteLine("Remarks: {0}", dbi.Message); } } } Console.ReadKey(); } } } ``` 结果: ``` Length: 4.5 Width: 7.5 Area: 33.75 Bug No: 49 Developer: Nuha Ali Last Reviewed: 10/10/2012 Remarks: Unused variable Bug No: 45 Developer: Zara Ali Last Reviewed: 12/8/2012 Remarks: Return type mismatch Bug No: 55, for Method: GetArea Developer: Zara Ali Last Reviewed: 19/10/2012 Remarks: Return type mismatch Bug No: 56, for Method: Display Developer: Zara Ali Last Reviewed: 19/10/2012 Remarks: Return Three ``` #####3.C#属性(Property) 属性(Property) 是类(class)、结构(structure)和接口(interface)的命名(named)成员。类或结构中的成员变量或方法称为 域(Field)。 属性(Property)是域(Field)的扩展,且可使用相同的语法来访问。 它们使用访问器(accessors让私有域的值可被读写或操作。 属性(Property)不会确定存储位置。相反,它们具有可读写或计算它们值的访问器(accessors)。 例如,有一个名为 Student 的类,带有 age、name 和 code的私有域。我们不能在类的范围以外直接访问这些域,但是我们可以拥有访问这些私有域的属性。 (1)访问器(Accessors) 属性(Property)的访问器(accessor)包含有助于获取(读取或计算)或设置(写入)属性的可执行语句。访问器(accessor)声明可包含一个 get 访问器、一个 set 访问器,或者同时包含二者。例如: ``` private int num = 3; public int Num { get { return num; } set { num = value; }//当我们实例化这个对象时,如果对obj.Num赋值的话,会执行set里面的程序,如果取得它的值的话,比如int x= obj.Num 会执行 get里面的语句 } ``` (2)抽象属性(Abstract Properties) 抽象类可拥有抽象属性,这些属性应在派生类中被实现。 ``` using System; using System.Collections.Generic; using System.Linq; using System.Text; using System.Threading.Tasks; namespace Property { class Program { public abstract class Person { public abstract string Name { get; set; } public abstract int Age { get; set; } } class Student : Person { private string code = "N.A"; private string name = "N.A"; private int age = 0; // 声明类型为 string 的 Code 属性 public string Code { get { return code; } set { code = value; } } // 声明类型为 string 的 Name 属性 public override string Name { get { return name; } set { name = value; } } // 声明类型为 int 的 Age 属性 public override int Age { get { return age; } set { age = value; } } public override string ToString() { return "Code = " + Code + ", Name = " + Name + ", Age = " + Age; } } static void Main(string[] args) { // 创建一个新的 Student 对象 Student s = new Student(); // 设置 student 的 code、name 和 age s.Code = "001"; s.Name = "Zara"; s.Age = 9; Console.WriteLine("Student Info:- {0}", s); // 增加年龄 s.Age += 1; Console.WriteLine("Student Info:- {0}", s); Console.ReadKey(); } } } ``` 结果: ``` Student Info: Code = 001, Name = Zara, Age = 9 Student Info: Code = 001, Name = Zara, Age = 10 ``` ####第二节C#的委托、事件及泛型 #####1. C#委托(Delegate) C#中的委托(Delegate)类似于C或C++中函数的指针。委托(Delegate)是存有对某个方法的引用的一种引用类型变量。引用可在运行时被改变。 委托(Delegate)特别用于实现事件和回调方法。所有的委托(Delegate)都派生自 System.Delegate 类。 (1)声明委托(Delegate) 委托声明决定了可由该委托引用的方法。委托可指向一个与其具有相同标签的方法。 ``` public delegate int MyDelegate(string s); ``` (2)实例化委托(Delegate) 一旦声明了委托类型,委托对象必须使用new关键字来创建,且与一个特定的方法有关。当创建委托时,传递到new语句的参数就像方法调用一样书写,但是不带有参数。 ``` public delegate void printString(string s); ... printString ps1 = new printString(WriteToScreen); printString ps2 = new printString(WriteToFile); ``` (3)委托的多播(Multicasting of a Delegate) 委托对象可使用 "+" 运算符进行合并。一个合并委托调用它所合并的两个委托。只有相同类型的委托可被合并。"-" 运算符可用于从合并的委托中移除组件委托。 使用委托的这个有用的特点,您可以创建一个委托被调用时要调用的方法的调用列表。这被称为委托的多播(multicasting),也叫组播。 ``` using System; namespace MyDelegate { class Program { delegate int NumberChanger(int n); static int num = 10; public static int AddNum(int p) { num += p; return num; } public static int MultNum(int q) { num *= q; return num; } public static int getNum() { return num; } static void Main(string[] args) { // 创建委托实例 NumberChanger nc; NumberChanger nc1 = new NumberChanger(AddNum); NumberChanger nc2 = new NumberChanger(MultNum); nc = nc1; nc += nc2; // 调用多播 nc(5); Console.WriteLine("Value of Num: {0}", getNum()); Console.ReadKey(); } } } ``` 结果: ``` Value of Num: 75 ``` (4)委托(Delegate)的用途 下面的实例演示了委托的用法。委托 printString 可用于引用带有一个字符串作为输入的方法,并不返回任何东西。 我们使用这个委托来调用两个方法,第一个把字符串打印到控制台,第二个把字符串打印到文件: ``` using System; using System.IO; namespace MyDelegate { class Program { static FileStream fs; static StreamWriter sw; // 委托声明 public delegate void printString(string s); // 该方法打印到控制台 public static void WriteToScreen(string str) { Console.WriteLine("The String is: {0}", str); } // 该方法打印到文件 public static void WriteToFile(string s) { fs = new FileStream("D:\\message.txt", FileMode.Append, FileAccess.Write); sw = new StreamWriter(fs); sw.WriteLine(s); sw.Flush(); sw.Close(); fs.Close(); } // 该方法把委托作为参数,并使用它调用方法 public static void sendString(printString ps) { ps("Hello World"); } static void Main(string[] args) { printString ps1 = new printString(WriteToScreen); printString ps2 = new printString(WriteToFile); sendString(ps1); sendString(ps2); Console.ReadKey(); } } } ``` 结果: ``` The String is: Hello World ``` #####2、C# 事件(Event) 事件(Event)基本上说是一个用户操作,如按键、点击、鼠标移动等等,或者是一些出现,如系统生成的通知。应用程序需要在事件发生时响应事件。例如,中断。事件是用于进程间通信。 (1)通过事件使用委托 事件在类中声明且生成,且通过使用同一个类或其他类中的委托与事件处理程序关联。包含事件的类用于发布事件。这被称为发布器(publisher)类。其他接受该事件的类被称为订阅器(subscriber) 类。事件使用 发布-订阅(publisher-subscriber)模型。 发布器(publisher)是一个包含事件和委托定义的对象。事件和委托之间的联系也定义在这个对象中。发布器(publisher)类的对象调用这个事件,并通知其他的对象。 订阅器(subscriber)是一个接受事件并提供事件处理程序的对象。在发布器(publisher)类中的委托调用订阅器(subscriber)类中的方法(事件处理程序)。 (2)声明事件(Event) 在类的内部声明事件,首先必须声明该事件的委托类型。 ``` public delegate void BoilerLogHandler(string status); ``` 然后,声明事件本身,使用 event 关键字: ``` // 基于上面的委托定义事件 public event BoilerLogHandler BoilerEventLog; ``` 本实例提供一个简单的用于热水锅炉系统故障排除的应用程序。当维修工程师检查锅炉时,锅炉的温度和压力会随着维修工程师的备注自动记录到日志文件中。 ``` using System; using System.IO; namespace BoilerEventAppl { // boiler 类 class Boiler { private int temp; private int pressure; public Boiler(int t, int p) { temp = t; pressure = p; } public int getTemp() { return temp; } public int getPressure() { return pressure; } } // 事件发布器 class DelegateBoilerEvent { public delegate void BoilerLogHandler(string status); // 基于上面的委托定义事件 public event BoilerLogHandler BoilerEventLog; public void LogProcess() { string remarks = "O. K"; Boiler b = new Boiler(100, 12); int t = b.getTemp(); int p = b.getPressure(); if (t > 150 || t < 80 || p < 12 || p > 15) { remarks = "Need Maintenance"; } OnBoilerEventLog("Logging Info:\n"); OnBoilerEventLog("Temparature " + t + "\nPressure: " + p); OnBoilerEventLog("\nMessage: " + remarks); } protected void OnBoilerEventLog(string message) { if (BoilerEventLog != null) { BoilerEventLog(message); } } } // 该类保留写入日志文件的条款 class BoilerInfoLogger { FileStream fs; StreamWriter sw; public BoilerInfoLogger(string filename) { fs = new FileStream(filename, FileMode.Append, FileAccess.Write); sw = new StreamWriter(fs); } public void Logger(string info) { sw.WriteLine(info); } public void Close() { sw.Flush(); sw.Close(); fs.Close(); } } class Program { static void Logger(string info) { Console.WriteLine(info); }//end of Logger static void Main(string[] args) { BoilerInfoLogger filelog = new BoilerInfoLogger("e:\\boiler.txt"); DelegateBoilerEvent boilerEvent = new DelegateBoilerEvent(); boilerEvent.BoilerEventLog += new DelegateBoilerEvent.BoilerLogHandler(Logger); boilerEvent.BoilerEventLog += new DelegateBoilerEvent.BoilerLogHandler(filelog.Logger); boilerEvent.LogProcess(); filelog.Close(); Console.ReadKey(); } } } ``` 结果: ``` Logging info: Temperature 100 Pressure 12 Message: O.K ``` C#中的委托是什么?事件是不是一种委托?事件和委托的关系。 委托可以把一个方法作为参数代入另一个方法。 委托可以理解为指向一个函数的指针。 委托和事件没有可比性,因为委托是类型,事件是对象,下面说的是委托的对象(用委托方式实现的事件)和(标准的event方式实现)事件的区别。事件的内部是用委托实现的。因为对于事件来讲,外部只能“注册自己 =、注销自己-=”,外界不可以注销其他的注册者,外界不可以主动触发事件。用Delegate就没法进行上面的控制,因此诞生了事件这种语法。事件是用来阉割委托实例的,类比用一个自定义类阉割List。事件只能add、remove自己,不能赋值。事件只能+ =、-=,不能= 。其实:事件内部就是一个private的委托和add、remove两个方法。 #####3、C# 泛型(Generic) 泛型(Generic)允许您延迟编写类或方法中的编程元素的数据类型的规范,直到实际在程序中使用它的时候。换句话说,泛型允许您编写一个可以与任何数据类型一起工作的类或方法。 您可以通过数据类型的替代参数编写类或方法的规范。当编译器遇到类的构造函数或方法的函数调用时,它会生成代码来处理指定的数据类型。 (1)泛型(Generic)方法 我们可以通过类型参数声明泛型方法。 ``` using System; namespace GenericApplication { class Program { static void Swap<T>(ref T lhs, ref T rhs) { T temp; temp = lhs; lhs = rhs; rhs = temp; } static void Main(string[] args) { int a, b; char c, d; a = 10; b = 20; c = 'I'; d = 'V'; // 在交换之前显示值 Console.WriteLine("Int values before calling swap:"); Console.WriteLine("a = {0}, b = {1}", a, b); Console.WriteLine("Char values before calling swap:"); Console.WriteLine("c = {0}, d = {1}", c, d); // 调用 swap Swap<int>(ref a, ref b); Swap<char>(ref c, ref d); // 在交换之后显示值 Console.WriteLine("Int values after calling swap:"); Console.WriteLine("a = {0}, b = {1}", a, b); Console.WriteLine("Char values after calling swap:"); Console.WriteLine("c = {0}, d = {1}", c, d); Console.ReadKey(); } } } ``` 结果: ``` Int values before calling swap: a = 10, b = 20 Char values before calling swap: c = I, d = V Int values after calling swap: a = 20, b = 10 Char values after calling swap: c = V, d = I ``` (2)泛型(Generic)委托 您可以通过类型参数定义泛型委托。 ``` delegate T NumberChanger<T>(T n); using System; namespace GenericDelegateApplic { delegate T NumberChanger<T>(T n); class Program { static int num = 10; public static int AddNum(int p) { num += p; return num; } public static int MultNum(int q) { num *= q; return num; } public static int getNum() { return num; } static void Main(string[] args) { // 创建委托实例 NumberChanger<int> nc1 = new NumberChanger<int>(AddNum); NumberChanger<int> nc2 = new NumberChanger<int>(MultNum); // 使用委托对象调用方法 nc1(25); Console.WriteLine("Value of Num: {0}", getNum()); nc2(5); Console.WriteLine("Value of Num: {0}", getNum()); Console.ReadKey(); } } } ``` 结果: ``` Value of Num: 35 Value of Num: 175 ``` ####第三节C#的索引器及匿名方法 #####1、C#索引器(Indexer) 索引器(Indexer)允许一个对象可以像数组一样被索引。当您为类定义一个索引器时,该类的行为就会像一个 虚拟数组(virtual array)一样。您可以使用数组访问运算符([ ])来访问该类的实例。 (1)语法 一维索引器的语法如下: ``` element-type this[int index] { // get 访问器 get { // 返回 index 指定的值 } // set 访问器 set { // 设置 index 指定的值 } } ``` (2)索引器(Indexer)的用途 索引器的行为的声明在某种程度上类似于属性(property)。就像属性(property),您可使用 get 和 set 访问器来定义索引器。但是,属性返回或设置一个特定的数据成员,而索引器返回或设置对象实例的一个特定值。换句话说,它把实例数据分为更小的部分,并索引每个部分,获取或设置每个部分。 定义一个属性(property)包括提供属性名称。索引器定义的时候不带有名称,但带有 this 关键字,它指向对象实例。 (3)重载索引器(Indexer) 索引器(Indexer)可被重载。索引器声明的时候也可带有多个参数,且每个参数可以是不同的类型。没有必要让索引器必须是整型的。C# 允许索引器可以是其他类型,例如,字符串类型。 ``` using System; namespace IndexerApplication { class IndexedNames { private string[] namelist = new string[size]; static public int size = 10; public IndexedNames() { for (int i = 0; i < size; i++) { namelist[i] = "N. A."; } } public string this[int index] { get { string tmp; if (index >= 0 && index <= size - 1) { tmp = namelist[index]; } else { tmp = ""; } return (tmp); } set { if (index >= 0 && index <= size - 1) { namelist[index] = value; } } } public int this[string name] { get { int index = 0; while (index < size) { if (namelist[index] == name) { return index; } index++; } return index; } } } class Program { static void Main(string[] args) { IndexedNames names = new IndexedNames(); names[0] = "Zara"; names[1] = "Riz"; names[2] = "Nuha"; names[3] = "Asif"; names[4] = "Davinder"; names[5] = "Sunil"; names[6] = "Rubic"; // 使用带有 int 参数的第一个索引器 for (int i = 0; i < IndexedNames.size; i++) { Console.WriteLine(names[i]); } // 使用带有 string 参数的第二个索引器 Console.WriteLine(names["Nuha"]); Console.ReadKey(); } } } ``` 结果: ``` Zara Riz Nuha Asif Davinder Sunil Rubic N. A. N. A. N. A. 2 ``` 2、C# 匿名方法 我们已经提到过,委托是用于引用与其具有相同标签的方法。换句话说,您可以使用委托对象调用可由委托引用的方法。 匿名方法(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); 是匿名方法的主体。 委托可以通过匿名方法调用,也可以通过命名方法调用,即,通过向委托对象传递方法参数。 ``` nc(10); ``` 完整例子: ``` using System; namespace DelegateApplication { delegate void NumberChanger(int n); class Program { static int num = 10; public static void AddNum(int p) { num += p; Console.WriteLine("Named Method: {0}", num); } public static void MultNum(int q) { num *= q; Console.WriteLine("Named Method: {0}", num); } public static int getNum() { return num; } static void Main(string[] args) { // 使用匿名方法创建委托实例 NumberChanger nc = delegate(int x) { Console.WriteLine("Anonymous Method: {0}", x); }; // 使用匿名方法调用委托 nc(10); // 使用命名方法实例化委托 nc = new NumberChanger(AddNum); // 使用命名方法调用委托 nc(5); // 使用另一个命名方法实例化委托 nc = new NumberChanger(MultNum); // 使用命名方法调用委托 nc(2); Console.ReadKey(); } } } ``` 结果: ``` Anonymous Method: 10 Named Method: 15 Named Method: 30 ``` ####第四节C#的不安全代码和多线程 #####1、C#不安全代码 当一个代码块使用 unsafe 修饰符标记时,C# 允许在函数中使用指针变量。不安全代码或非托管代码是指使用了指针变量的代码块。 (1)指针变量++ 指针是指为另一个变量的地址的变量,即,内存位置的直接地址。就像其他变量或常量,您必须在使用指针存储其他变量地址之前声明指针。 指针变量声明的一般形式为: ``` type* var-name; ``` 下面是指针类型声明的实例: 实例|描述 -|- int* p|p 是指向整数的指针。 double* p|p 是指向双精度数的指针。 float* p|p 是指向浮点数的指针。 int** p|p 是指向整数的指针的指针。 int*[] p|p 是指向整数的指针的一维数组。 char* p|p 是指向字符的指针。 void* p|p 是指向未知类型的指针。 在同一个声明中声明多个指针时,星号 * 仅与基础类型一起写入;而不是用作每个指针名称的前缀。 ``` int* p1, p2, p3; // 正确 int *p1, *p2, *p3; // 错误 ``` 下面的实例说明了 C# 中使用了 属性|描述 -|- CurrentContext|获取线程正在其中执行的当前上下文。 CurrentCulture|获取或设置当前线程的区域性。 CurrentPrinciple|获取或设置线程的当前负责人(对基于角色的安全性而言)。 CurrentThread|获取当前正在运行的线程。 CurrentUICulture|获取或设置资源管理器使用的当前区域性以便在运行时查找区域性特定的资源。 ExecutionContext|获取一个 ExecutionContext 对象,该对象包含有关当前线程的各种上下文的信息。 IsAlive|获取一个值,该值指示当前线程的执行状态。 IsBackground|获取或设置一个值,该值指示某个线程是否为后台线程。 IsThreadPoolThread|获取一个值,该值指示线程是否属于托管线程池。 ManagedThreadId|获取当前托管线程的唯一标识符。 Name|获取或设置线程的名称。 Priority|获取或设置一个值,该值指示线程的调度优先级。 ThreadState|获取一个值,该值包含当前线程的状态。 下表列出了Thread类的一些常用的方法: unsafe修饰符时指针的使用: ``` using System; namespace UnsafeCodeApplication { class Program { static unsafe void Main(string[] args) { int var = 20; int* p = &var; Console.WriteLine("Data is: {0} ", var); Console.WriteLine("Address is: {0}", (int)p); Console.ReadKey(); } } } ``` 结果: ``` Data is: 20 Address is: 99215364 ``` (2)使用指针检索数据值 您可以使用 ToString()方法检索存储在指针变量所引用位置的数据。 ``` using System; namespace UnsafeCodeApplication { class Program { public static void Main() { unsafe { int var = 20; int* p = &var; Console.WriteLine("Data is: {0} " , var); Console.WriteLine("Data is: {0} " , p->ToString()); Console.WriteLine("Address is: {0} " , (int)p); } Console.ReadKey(); } } } ``` 结果: ``` Data is: 20 Data is: 20 Address is: 77128984 ``` (3)传递指针作为方法的参数 您可以向方法传递指针变量作为方法的参数。 ``` using System; namespace UnsafeCodeApplication { class TestPointer { public unsafe void swap(int* p, int *q) { int temp = *p; *p = *q; *q = temp; } public unsafe static void Main() { TestPointer p = new TestPointer(); int var1 = 10; int var2 = 20; int* x = &var1; int* y = &var2; Console.WriteLine("Before Swap: var1:{0}, var2: {1}", var1, var2); p.swap(x, y); Console.WriteLine("After Swap: var1:{0}, var2: {1}", var1, var2); Console.ReadKey(); } } } ``` 结果: ``` Before Swap: var1: 10, var2: 20 After Swap: var1: 20, var2: 10 ``` (4)使用指针访问数组元素 在 C# 中,数组名称和一个指向与数组数据具有相同数据类型的指针是不同的变量类型。例如,int *p 和 int[] p是不同的类型。您可以增加指针变量p,因为它在内存中不是固定的,但是数组地址在内存中是固定的,所以您不能增加数组p。 因此,如果您需要使用指针变量访问数组数据,可以像我们通常在C或C++中所做的那样,使用 fixed关键字来固定指针。 ``` using System; namespace UnsafeCodeApplication { class TestPointer { public unsafe static void Main() { int[] list = {10, 100, 200}; fixed(int *ptr = list) /* 显示指针中数组地址 */ for ( int i = 0; i < 3; i++) { Console.WriteLine("Address of list[{0}]={1}",i,(int)(ptr + i)); Console.WriteLine("Value of list[{0}]={1}", i, *(ptr + i)); } Console.ReadKey(); } } } ``` 结果: ``` Address of list[0] = 31627168 Value of list[0] = 10 Address of list[1] = 31627172 Value of list[1] = 100 Address of list[2] = 31627176 Value of list[2] = 200 ``` (5)编译不安全代码 为了编译不安全代码,您必须切换到命令行编译器指定/unsafe 命令行。 例如,为了编译包含不安全代码的名为prog1.cs 的程序,需在命令行中输入命令: ``` csc /unsafe prog1.cs ``` 如果您使用的是 Visual Studio IDE,那么您需要在项目属性中启用不安全代码。 步骤如下: 通过双击资源管理器(Solution Explorer)中的属性(properties)节点,打开项目属性(project properties)。 点击 Build 标签页。 选择选项"Allow unsafe code"。 #####2、C#多线程 线程被定义为程序的执行路径。每个线程都定义了一个独特的控制流。如果您的应用程序涉及到复杂的和耗时的操作,那么设置不同的线程执行路径往往是有益的,每个线程执行特定的工作。 线程是轻量级进程。一个使用线程的常见实例是现代操作系统中并行编程的实现。使用线程节省了CPU周期的浪费,同时提高了应用程序的效率。 到目前为止我们编写的程序是一个单线程作为应用程序的运行实例的单一的过程运行的。但是,这样子应用程序同时只能执行一个任务。为了同时执行多个任务,它可以被划分为更小的线程。 (1)线程生命周期 线程生命周期开始于System.Threading.Thread 类的对象被创建时,结束于线程被终止或完成执行时。 下面列出了线程生命周期中的各种状态: (1 未启动状态:当线程实例被创建但 Start 方法未被调用时的状况。 (2 就绪状态:当线程准备好运行并等待CPU 周期时的状况。 (3 不可运行状态:下面的几种情况下线程是不可运行的: - 已经调用 Sleep 方法 - 已经调用 Wait 方法 - 通过 I/O 操作阻塞 - 死亡状态:当线程已完成执行或已中止时的状况。 (2)主线程 在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 ``` (3)Thread类常用的属性和方法 下表列出了Thread类的一些常用的属性: 序号|方法名 | 描述 -|-|- 1|public void Abort()|在调用此方法的线程上引发 ThreadAbortException,以开始终止此线程的过程。调用此方法通常会终止线程。 2|public static LocalDataStoreSlot AllocateDataSlot()|在所有的线程上分配未命名的数据槽。为了获得更好的性能,请改用以 ThreadStaticAttribute 属性标记的字段。 3|public static LocalDataStoreSlot AllocateNamedDataSlot( string name)|在所有线程上分配已命名的数据槽。为了获得更好的性能,请改用以 ThreadStaticAttribute 属性标记的字段。 4|public static void BeginCriticalRegion()|通知主机执行将要进入一个代码区域,在该代码区域内线程中止或未经处理的异常的影响可能会危害应用程序域中的其他任务。 5|public static void BeginThreadAffinity()|通知主机托管代码将要执行依赖于当前物理操作系统线程的标识的指令。 6|public static void EndCriticalRegion()|通知主机执行将要进入一个代码区域,在该代码区域内线程中止或未经处理的异常仅影响当前任务。 7|public static void EndThreadAffinity()|通知主机托管代码已执行完依赖于当前物理操作系统线程的标识的指令。 8|public static void FreeNamedDataSlot(string name)|为进程中的所有线程消除名称与槽之间的关联。为了获得更好的性能,请改用以 ThreadStaticAttribute 属性标记的字段。 9|public static Object GetData( LocalDataStoreSlot slot ) |在当前线程的当前域中从当前线程上指定的槽中检索值。为了获得更好的性能,请改用以 ThreadStaticAttribute 属性标记的字段。 10|public static AppDomain GetDomain()|返回当前线程正在其中运行的当前域。 11|public static AppDomain GetDomainID()|返回唯一的应用程序域标识符。 12|public static LocalDataStoreSlot GetNamedDataSlot( string name ) |查找已命名的数据槽。为了获得更好的性能,请改用以 ThreadStaticAttribute 属性标记的字段。 13|public void Interrupt()|中断处于 WaitSleepJoin 线程状态的线程。 14|public void Join()|在继续执行标准的 COM 和 SendMessage 消息泵处理期间,阻塞调用线程,直到某个线程终止为止。此方法有不同的重载形式。 15|public static void MemoryBarrier()|按如下方式同步内存存取:执行当前线程的处理器在对指令重新排序时,不能采用先执行 MemoryBarrier 调用之后的内存存取,再执行 MemoryBarrier 调用之前的内存存取的方式。 16|public static void ResetAbort()|取消为当前线程请求的 Abort。 17|public static void SetData( LocalDataStoreSlot slot, Object data )|在当前正在运行的线程上为此线程的当前域在指定槽中设置数据。为了获得更好的性能,请改用以 ThreadStaticAttribute 属性标记的字段。 18|public void Start()|开始一个线程。 19|public static void Sleep( int millisecondsTimeout )|让线程暂停一段时间。 20|public static void SpinWait( int iterations )|导致线程等待由 iterations 参数定义的时间量。 21|public static byte VolatileRead( ref byte address ) ;public static double VolatileRead( ref double address )public static int VolatileRead( ref int address );public static Object VolatileRead( ref Object address )|读取字段值。无论处理器的数目或处理器缓存的状态如何,该值都是由计算机的任何处理器写入的最新值。此方法有不同的重载形式。这里只给出了一些形式。 22|public static void VolatileWrite( ref byte address, byte value ),public static void VolatileWrite( ref double address, double value );public static void VolatileWrite( ref int address, int value );public static void VolatileWrite( ref Object address, Object value )|立即向字段写入一个值,以使该值对计算机中的所有处理器都可见。此方法有不同的重载形式。这里只给出了一些形式。 23|public static bool Yield()|导致调用线程执行准备好在当前处理器上运行的另一个线程。由操作系统选择要执行的线程。 (4)创建线程 线程是通过扩展 Thread类创建的。扩展的 Thread 类调用 Start() 方法来开始子线程的执行。 ``` using System; using System.Threading; namespace MultithreadingApplication { class ThreadCreationProgram { public static void CallToChildThread() { Console.WriteLine("Child thread starts"); } static void Main(string[] args) { ThreadStart childref = new ThreadStart(CallToChildThread); Console.WriteLine("In Main: Creating the Child thread"); Thread childThread = new Thread(childref); childThread.Start(); Console.ReadKey(); } } } ``` 结果: ``` In Main: Creating the Child thread Child thread starts ``` (5)管理线程 Thread 类提供了各种管理线程的方法。 下面的实例演示了sleep()方法的使用,用于在一个特定的时间暂停线程。 ``` using System; using System.Threading; namespace MultithreadingApplication { class ThreadCreationProgram { public static void CallToChildThread() { Console.WriteLine("Child thread starts"); // 线程暂停 5000 毫秒 int sleepfor = 5000; Console.WriteLine("Child Thread Paused for {0} seconds", sleepfor / 1000); Thread.Sleep(sleepfor); Console.WriteLine("Child thread resumes"); } static void Main(string[] args) { ThreadStart childref = new ThreadStart(CallToChildThread); Console.WriteLine("In Main: Creating the Child thread"); Thread childThread = new Thread(childref); childThread.Start(); Console.ReadKey(); } } } ``` 结果: ``` In Main: Creating the Child thread Child thread starts Child Thread Paused for 5 seconds Child thread resumes ``` (6)销毁线程 Abort()方法用于销毁线程。 通过抛出threadabortexception在运行时中止线程。这个异常不能被捕获,获得的是(正在中止线程),如果有finally块,控制会被送至finally块。 ``` using System; using System.Threading; namespace MultithreadingApplication { class ThreadCreationProgram { public static void CallToChildThread() { try { Console.WriteLine("Child thread starts"); // 计数到 10 for (int counter = 0; counter <= 10; counter++) { Thread.Sleep(500); Console.WriteLine(counter); } Console.WriteLine("Child Thread Completed"); } catch (ThreadAbortException e) { Console.WriteLine("Thread Abort Exception"); } finally { Console.WriteLine("Couldn't catch the Thread Exception"); } } static void Main(string[] args) { ThreadStart childref = new ThreadStart(CallToChildThread); Console.WriteLine("In Main: Creating the Child thread"); Thread childThread = new Thread(childref); childThread.Start(); // 停止主线程一段时间 Thread.Sleep(2000); // 现在中止子线程 Console.WriteLine("In Main: Aborting the Child thread"); childThread.Abort(); Console.ReadKey(); } } } ``` 结果: ``` In Main: Creating the Child thread Child thread starts 0 1 2 In Main: Aborting the Child thread Thread Abort Exception Couldn't catch the Thread Exception ``` ####第五节正则表达式 #####1.介绍 正则表达式是一种匹配输入文本的模式。.Net 框架提供了允许这种匹配的正则表达式引擎。模式由一个或多个字符、运算符和结构组成。 #####2.正则表达式基本用法 Regex Tester是PyCharm的第三方插件,可以测试正则表达式,这节课就使用PyCharm来教大家,不知道如何安装请看机遇屋的Python课程第一章第一节。安装Regex Tester:Setting-》Plugins-》Browser Repositories-》输入Regex Tester-》点击安装-》重启PyCharm。 重启后进入:点击PyCharm界面左下方的小矩形按钮找到Regex Tester选项点击进入。 (1)常用讲解 1)使用 ``` \: 将下一个字符标记为一个特殊字符. d # 普通字符 \d # 0-9 正整数 s # 普通字符 \s # 空白符,比如\t,\n ``` 2)定界符 ``` ^: 匹配输入字符串的开始位置 $: 匹配输入字符串的结束位置 正则: ^269.*269$ # 匹配269hello269 \b: 匹配一个单词边界,也就是指单词和空格间的位置 per Ch 正则: er\b # 可以匹配出er \B: 匹配非单词边界 vierb 正则: er\B # 可以匹配出er ``` 3)个数/次数 ``` *: 匹配前面的子表达式零次或多次 +: 匹配前面的子表达式一次或多次 ?: 匹配前面的子表达式零次或一次 {n}: n 是一个非负整数 {n,}: n 是一个非负整数 {n,m}: m 和 n 均为非负整数,其中n <= m heo helo hello helllo 正则: hel*o # 可以匹配 heo helo hello helllo 正则: hel+o # 可以匹配 helo hello helllo 正则: hel?o # 可以匹配 heo helo 正则: hel{3}o # 可以匹配 helllo 正则: hel{1,}o # 可以匹配 helo hello helllo 正则: hel{0,3}o # 可以匹配 heo helo hello helllo ?: 当该字符紧跟在任何一个其他限制符 (*, +, ?, {n}, {n,}, {n,m}) 后面时,匹配模式是非贪婪的 hel hell helll 正则: hel+ # 可以匹配 hel hell helll 正则: hel+? # 只会匹配 hel hell helll中的 hel部分 .: 匹配除 "\n" 之外的任何单个字符 hello world python 正则: .* # 可以匹配出所有单词 x|y: 匹配 x 或 y [xyz]: 字符集合 hello aello 正则: a|hello # 匹配 hello 和 a 正则: [a|h]ello # 匹配 hello 和 aello ``` 4)范围 ``` [xyz]: 字符集合 [^xyz]: 字符集合取反 [a-z]: 字符范围 [^a-z]:非a-z字符范围 ``` 5)空白符 ``` \f: 匹配一个换页符 \n: 匹配一个换行符 \r: 匹配一个回车符 \t: 匹配一个制表符 ``` 6)语法糖 ``` \d: 匹配一个数字字符. 相当于 [0-9] \D: 匹配一个非数字字符. 相当于 [^0-9] \s: 匹配任何空白字符,包括空格、制表符、换页符等等. 相当于 [\f\n\t\r\v] \S: 匹配任何非空白字符. 相当于 [^\f\n\t\r\v] \w: 匹配字母、数字、下划线. 相当于 [A-Za-z0-9_] \W: 匹配非字母、数字、下划线. 相当于 [^A-Za-z0-9_] ``` (2)延伸(可不看) 用于定义正则表达式的各种类别的字符、运算符和结构:字符转义、字符类、定位点、分组构造、限定符、反向引用构造、备用构造、替换、杂项构造。下面我参考网上资料整理了一下有哪些,大家不必刻意去记忆,达到会用的目的就好。 (1字符转义: 转义字符|描述|模式|匹配 -|-|-|- \a|与报警 (bell) 符 \u0007 匹配。|\a|"Warning!" + '\u0007' 中的 "\u0007" \b|在字符类中,与退格键 \u0008 匹配。|[\b]{3,}|"\b\b\b\b" 中的 "\b\b\b\b" \t|与制表符 \u0009 匹配。|(\w+)\t|"Name\tAddr\t" 中的 "Name\t" 和 "Addr\t" \r|与回车符 \u000D 匹配。(\r 与换行符 \n 不是等效的。)|\r\n(\w+)|"\r\Hello\nWorld." 中的 "\r\nHello" \v|与垂直制表符 \u000B 匹配。|[\v]{2,}|"\v\v\v" 中的 "\v\v\v" \f|与换页符 \u000C 匹配。|[\f]{2,}|"\f\f\f" 中的 "\f\f\f" \n|与换行符 \u000A 匹配。|\r\n(\w+)|"\r\Hello\nWorld." 中的 "\r\nHello" \e|与转义符 \u001B 匹配。|\e|"\x001B" 中的 "\x001B" \ nnn|使用八进制表示形式指定一个字符(nnn 由二到三位数字组成)。|\w\040\w|"a bc d" 中的 "a b" 和 "c d" \x nn|使用十六进制表示形式指定字符(nn 恰好由两位数字组成)。|\w\x20\w|"a bc d" 中的 "a b" 和 "c d" \c X \c x|匹配 X 或 x 指定的 ASCII 控件字符,其中 X 或 x 是控件字符的字母。|\cC "\x0003" 中的 "\x0003" (Ctrl-C) \u nnnn|使用十六进制表示形式匹配一个 Unicode 字符(由 nnnn 表示的四位数)。|\w\u0020\w "a bc d" 中的 "a b" 和 "c d" |在后面带有不识别的转义字符时,与该字符匹配。|\d+[\+-x\*]\d+\d+[\+-x\*\d+ "(2+2) * 3*9" 中的 "2+2" 和 "3*9" (2字符类: 字符类与一组字符中的任何一个字符匹配。 字符类|描述|模式|匹配 -|-|-|- [character_group]|匹配 character_group 中的任何单个字符。 默认情况下,匹配区分大小写。|[mn]|"mat" 中的 "m","moon" 中的 "m" 和 "n" [^character_group]|非:与不在 character_group 中的任何单个字符匹配。 默认情况下,character_group 中的字符区分大小写。|[^aei]|"avail" 中的 "v" 和 "l" [ first - last ]|字符范围:与从 first 到 last 的范围中的任何单个字符匹配。|(\w+)\t|"Name\tAddr\t" 中的 "Name\t" 和 "Addr\t" .|通配符:与除 \n 之外的任何单个字符匹配。 若要匹配原意句点字符(. 或 \u002E),您必须在该字符前面加上转义符 (\.)。|a.e|"have" 中的 "ave", "mate" 中的 "ate" \p{ name }|与 name 指定的 Unicode 通用类别或命名块中的任何单个字符匹配。|\p{Lu}|"City Lights" 中的 "C" 和 "L" \P{ name }|与不在 name 指定的 Unicode 通用类别或命名块中的任何单个字符匹配。|\P{Lu}|"City" 中的 "i"、 "t" 和 "y" \w|与任何单词字符匹配。|\w|"Room#1" 中的 "R"、 "o"、 "m" 和 "1" \W|与任何非单词字符匹配。|\W|"Room#1" 中的 "#" \s|与任何空白字符匹配。|\w\s|"ID A1.3" 中的 "D " \S|与任何非空白字符匹配。|\s\S|"int __ctr" 中的 " _" \d|与任何十进制数字匹配。|\d|"4 = IV" 中的 "4" \D|匹配不是十进制数的任意字符。|\D|"4 = IV" 中的 " "、 "="、 " "、 "I" 和 "V" (3定位点: 定位点或原子零宽度断言会使匹配成功或失败,具体取决于字符串中的当前位置,但它们不会使引擎在字符串中前进或使用字符。 断言|描述|模式|匹配 -|-|-|- ^|匹配必须从字符串或一行的开头开始。|^\d{3}|"567-777-" 中的 "567" $|匹配必须出现在字符串的末尾或出现在行或字符串末尾的 \n 之前。|-\d{4}$|"8-12-2012" 中的 "-2012" \A|匹配必须出现在字符串的开头。|\A\w{4}|"Code-007-" 中的 "Code" \Z|匹配必须出现在字符串的末尾或出现在字符串末尾的 \n 之前。|-\d{3}\Z|"Bond-901-007" 中的 "-007" \z|匹配必须出现在字符串的末尾。|-\d{3}\z|"-901-333" 中的 "-333" \G|匹配必须出现在上一个匹配结束的地方。|\G\(\d\)|"(1)(3)(5)[7](9)" 中的 "(1)"、 "(3)" 和 "(5)" \b|匹配一个单词边界,也就是指单词和空格间的位置。|er\b|匹配"never"中的"er",但不能匹配"verb"中的"er"。 \B|匹配非单词边界。|er\B|匹配"verb"中的"er",但不能匹配"never"中的"er"。 (4分组构造: 分组构造描述了正则表达式的子表达式,通常用于捕获输入字符串的子字符串。 分组构造|描述|模式|匹配 -|-|-|- ( subexpression )|捕获匹配的子表达式并将其分配到一个从零开始的序号中。|(\w)\1|"deep" 中的 "ee" (?< name >subexpression)|将匹配的子表达式捕获到一个命名组中。|(?< double>\w)\k< double>|"deep" 中的 "ee" (?< name1 -name2 >subexpression)|定义平衡组定义。|(((?'Open'\()[^\(\)]*)+((?'Close-Open'\))[^\(\)]*)+)*(?(Open)(?!))$|"3+2^((1-3)*(3-1))" 中的 "((1-3)*(3-1))" (?: subexpression)|定义非捕获组。|Write(?:Line)?|"Console.WriteLine()" 中的 "WriteLine" (?imnsx-imnsx:subexpression)|应用或禁用 subexpression 中指定的选项。|A\d{2}(?i:\w+)\b|"A12xl A12XL a12xl" 中的 "A12xl" 和 "A12XL" (?= subexpression)|零宽度正预测先行断言。|\w+(?=\.)|"He is. The dog ran. The sun is out." 中的 "is"、 "ran" 和 "out" (?! subexpression)|零宽度负预测先行断言。|\b(?!un)\w+\b|"unsure sure unity used" 中的 "sure" 和 "used" (?<=subexpression)|零宽度正回顾后发断言。|(?<=19)\d{2}\b|"1851 1999 1950 1905 2003" 中的 "99"、"50"和 "05" (?<! subexpression)|零宽度负回顾后发断言。|(?|"Hi woman Hi man" 中的 "man" (?> subexpression)|非回溯(也称为"贪婪")子表达式。|[13579](?>A+B+)|"1ABB 3ABBC 5AB 5AC" 中的 "1ABB"、 "3ABB" 和 "5AB" (5限定符: 限定符指定在输入字符串中必须存在上一个元素(可以是字符、组或字符类)的多少个实例才能出现匹配项。 限定符包括下表中列出的语言元素。 限定符|描述|模式|匹配 -|-|-|- *|匹配上一个元素零次或多次。|\d*\.\d|".0"、 "19.9"、 "219.9" +|匹配上一个元素一次或多次。|"be+"|"been" 中的 "bee", "bent" 中的 "be" ?|匹配上一个元素零次或一次。|"rai?n"|"ran"、 "rain" { n }|匹配上一个元素恰好 n 次。|",\d{3}"|"1,043.6" 中的 ",043", "9,876,543,210" 中的 ",876"、 ",543" 和 ",210" { n ,}|匹配上一个元素至少 n 次。|"\d{2,}"|"166"、 "29"、 "1930" { n , m }|匹配上一个元素至少 n 次,但不多于 m 次。|"\d{3,5}"|"166", "17668", "193024" 中的 "19302" *?|匹配上一个元素零次或多次,但次数尽可能少。|\d*?\.\d|".0"、 "19.9"、 "219.9" +?|匹配上一个元素一次或多次,但次数尽可能少。|"be+?"|"been" 中的 "be", "bent" 中的 "be" ??|匹配上一个元素零次或一次,但次数尽可能少。|"rai??n"|"ran"、 "rain" { n }?|匹配前导元素恰好 n 次。|",\d{3}?"|"1,043.6" 中的 ",043", "9,876,543,210" 中的 ",876"、 ",543" 和 ",210" { n ,}?|匹配上一个元素至少 n 次,但次数尽可能少。|"\d{2,}?"|"166"、 "29" 和 "1930" { n , m }?|匹配上一个元素的次数介于 n 和 m 之间,但次数尽可能少。|"\d{3,5}?"|"166", "17668", "193024" 中的 "193" 和 "024" (6反向引用构造: 反向引用允许在同一正则表达式中随后标识以前匹配的子表达式。 反向引用构造|描述|模式|匹配 -|-|-|- \ number|反向引用。 匹配编号子表达式的值。|(\w)\1|"seek" 中的 "ee" \k< name >|命名反向引用。 匹配命名表达式的值。|(?< char>\w)\k< char>|"seek" 中的 "ee" (7备用构造: 备用构造用于修改正则表达式以启用 either/or 匹配。 备用构造|描述|模式|匹配 -|-|-|- ||匹配以竖线 (|) 字符分隔的任何一个元素。|th(e|is|at)|"this is the day. " 中的 "the" 和 "this" (?( expression )yes | no )|如果正则表达式模式由 expression 匹配指定,则匹配 yes;否则匹配可选的 no 部分。 expression 被解释为零宽度断言。|(?(A)A\d{2}\b|\b\d{3}\b)|"A10 C103 910" 中的 "A10" 和 "910" (?( name )yes | no )|如果 name 或已命名或已编号的捕获组具有匹配,则匹配 yes;否则匹配可选的 no。|(?< quoted>")?(?(quoted).+?"|\S+\s)|"Dogs.jpg "Yiska playing.jpg"" 中的 Dogs.jpg 和 "Yiska playing.jpg" (8替换: 替换是替换模式中使用的正则表达式。 字符|描述|模式|替换模式|输入字符串|结果字符串 -|-|-|-|-|- $number|替换按组 number 匹配的子字符串。|\b(\w+)(\s)(\w+)\b|$3$2$1|"one two"|"two one" ${name}|替换按命名组 name 匹配的子字符串。|\b(?< word1>\w+)(\s)(?< word2>\w+)\b|${word2} ${word1}|"one two"|"two one" $$|替换字符"$"。|\b(\d+)\s?USD|$$$1|"103 USD"|"$103" $&|替换整个匹配项的一个副本。|(\$*(\d*(\.+\d+)?){1})|**$&|"$1.30"|"**$1.30**" $`|替换匹配前的输入字符串的所有文本。|B+|$`|"AABBCC"|"AAAACC" $'|替换匹配后的输入字符串的所有文本。|B+|$'|"AABBCC"|"AACCCC" $+|替换最后捕获的组。|B+(C+)|$+|"AABBCCDD"|AACCDD $_|替换整个输入字符串。|B+|$_|"AABBCC"|"AAAABBCCCC" (9杂项构造: 各种杂项构造。 构造|描述|实例 -|-|- (?imnsx-imnsx)|在模式中间对诸如不区分大小写这样的选项进行设置或禁用。|\bA(?i)b\w+\b 匹配 "ABA Able Act" 中的 "ABA" 和 "Able" (?#注释)|内联注释。该注释在第一个右括号处终止。|\bA(?#匹配以A开头的单词)\w+\b # [行尾]|该注释以非转义的 # 开头,并继续到行的结尾。|(?x)\bA\w+\b#匹配以 A 开头的单词 (1) Regex 类 序号|方法 | 描述 -|-|- 1|public bool IsMatch( string input ) |指示 Regex 构造函数中指定的正则表达式是否在指定的输入字符串中找到匹配项。 2|public bool IsMatch( string input, int startat )|指示 Regex 构造函数中指定的正则表达式是否在指定的输入字符串中找到匹配项,从字符串中指定的开始位置开始。 3|public static bool IsMatch( string input, string pattern )|指示指定的正则表达式是否在指定的输入字符串中找到匹配项。 4|public MatchCollection Matches( string input )|在指定的输入字符串中搜索正则表达式的所有匹配项。 5|public string Replace( string input, string replacement )|在指定的输入字符串中,把所有匹配正则表达式模式的所有匹配的字符串替换为指定的替换字符串。 6|public string[] Split( string input )|把输入字符串分割为子字符串数组,根据在 Regex 构造函数中指定的正则表达式模式定义的位置进行分割。 #### 23种设计模式 在面向对象编程中,有23种被广泛接受和应用的设计模式,这些模式被称为GoF(Gang of Four)设计模式,它们是由Erich Gamma、Richard Helm、Ralph Johnson和John Vlissides在书籍《Design Patterns: Elements of Reusable Object-Oriented Software》中提出的。这些设计模式分为三大类:创建型、结构型和行为型。 以下是这23种设计模式的分类和简要说明,以及其中一些在C#开发中应用较多的模式: **创建型设计模式(Creational Patterns)**: 1. **单例模式(Singleton Pattern)**:确保一个类只有一个实例,并提供全局访问点。 2. **工厂方法模式(Factory Method Pattern)**:定义一个创建对象的接口,但由子类决定实例化哪个类。 3. **抽象工厂模式(Abstract Factory Pattern)**:提供一个创建一系列相关或相互依赖对象的接口,而无需指定其具体类。 4. **建造者模式(Builder Pattern)**:将一个复杂对象的构建与其表示分离,使得同样的构建过程可以创建不同的表示。 5. **原型模式(Prototype Pattern)**:通过复制已有对象来创建新对象,从而避免了创建新对象时的大量初始化工作。 **结构型设计模式(Structural Patterns)**: 6. **适配器模式(Adapter Pattern)**:将一个类的接口转换成客户希望的另一个接口,使得原本不兼容的类可以协同工作。 7. **桥接模式(Bridge Pattern)**:将抽象部分与它的实现部分分离,使它们都可以独立地变化。 8. **组合模式(Composite Pattern)**:将对象组合成树形结构以表示"部分-整体"的层次结构,使得用户对单个对象和组合对象的使用具有一致性。 9. **装饰器模式(Decorator Pattern)**:动态地给一个对象添加额外的职责,即为装饰。 10. **外观模式(Facade Pattern)**:提供一个统一的接口,用来访问子系统中的一群接口。 11. **享元模式(Flyweight Pattern)**:运用共享技术有效地支持大量细粒度的对象。 **行为型设计模式(Behavioral Patterns)**: 12. **责任链模式(Chain of Responsibility Pattern)**:将请求的发送和接收分离,以便多个对象都有机会处理这个请求。 13. **命令模式(Command Pattern)**:将一个请求封装成一个对象,从而使您可以用不同的请求对客户进行参数化。 14. **解释器模式(Interpreter Pattern)**:提供一个解释器,用来解释语法或表达式。 15. **迭代器模式(Iterator Pattern)**:提供一种方法访问一个容器对象中的各个元素,而又不需暴露该对象的内部细节。 16. **中介者模式(Mediator Pattern)**:用一个中介对象来封装一系列的对象交互,使各对象之间松耦合。 17. **备忘录模式(Memento Pattern)**:在不破坏封装的前提下,捕获一个对象的内部状态,并在该对象之外保存这个状态。 18. **观察者模式(Observer Pattern)**:定义了对象之间的一对多依赖关系,使得每当一个对象改变状态,依赖它的对象都会得到通知并自动更新。 19. **状态模式(State Pattern)**:允许对象在其内部状态改变时改变它的行为。 20. **策略模式(Strategy Pattern)**:定义一系列算法,将它们封装成独立的策略类,使得算法可以互相替换。 21. **模板方法模式(Template Method Pattern)**:定义一个算法的骨架,而将一些步骤延迟到子类中实现。 22. **访问者模式(Visitor Pattern)**:将数据结构和数据操作分离,使得可以在不改变数据结构的情况下增加新的操作。 以上是23种常见的设计模式,它们各自有不同的用途和适用场景。在C#开发中,一些常用的设计模式包括单例模式、工厂模式、适配器模式、观察者模式、策略模式等。选择合适的设计模式有助于提高代码的可维护性和扩展性。 #### 课程附件 [附件](https://movie.aaoit.com/upload/VIP/Video/admin//2018/5/2/c83aaaeec9889617a0da638b73e612d7.zip "附件")
上一篇
下一篇
版权所有:机遇屋在线 Copyright © 2021-2025 jiyuwu Co., Ltd.
鲁ICP备16042261号-1