字符串
在 C# 中,使用字符串作为字符数组,但更常见的做法是使用string关键字来声明一个字符串变量。string关键字是System.String类的别名。
创建一个字符串对象
可以使用以下方法之一来创建字符串对象:
- 通过将一个字符串文字分配给一个
String变量 - 通过使用
String类构造函数 - 通过使用字符串连接运算符(
+) - 通过检索属性或调用返回字符串的方法
- 通过调用格式化方法将值或对象转换为其字符串表示形式
示例:
using System;
namespace StringApplication
{
class Program
{
static void Main(string[] args)
{
//from string literal and string concatenation
string fname, lname;
fname = "Rowan";
lname = "Atkinson";
string fullname = fname + lname;
Console.WriteLine("Full Name: {0}", fullname);
//by using string constructor
char[] letters = { 'H', 'e', 'l', 'l','o' };
string greetings = new string(letters);
Console.WriteLine("Greetings: {0}", greetings);
//methods returning string
string[] sarray = { "Hello", "From", "Yiibai", "Point" };
string message = String.Join(" ", sarray);
Console.WriteLine("Message: {0}", message);
//formatting method to convert a value
DateTime waiting = new DateTime(2012, 10, 10, 17, 58, 1);
string chat = String.Format("Message sent at {0:t} on {0:D}", waiting);
Console.WriteLine("Message: {0}", chat);
}
}
}
当编译和执行上述代码时,会产生以下结果:
Full Name: Rowan Atkinson
Greetings: Hello
Message: Hello From Yiibai Point
Message: Message sent at 5:58 PM on Wednesday, October 10, 2012
String类的属性
String类具有以下两个属性:
| 编号 | 属性 | 说明 |
|---|---|---|
| 1 | Chars | 获取当前String对象中指定位置的Char对象。 |
| 2 | Length | 获取当前String对象中的字符数。 |
String类的方法
String类有许多方法可以帮助我们来处理字符串对象。下表中提供了一些最常用的方法:
| 编号 | 方法 | 说明 |
|---|---|---|
| 1 | public static int Compare(string strA, string strB) | 比较两个指定的字符串对象,并返回一个整数,指示其在排序顺序中的相对位置。 |
| 2 | public static int Compare(string strA, string strB, bool ignoreCase ) | 比较两个指定的字符串对象,并返回一个整数,指示其在排序顺序中的相对位置。 但是,如果布尔参数为真,它将忽略大小写。 |
| 3 | public static string Concat(string str0, string str1) | 连接两个字符串对象。 |
| 4 | public static string Concat(string str0, string str1, string str2) | 连接三个字符串对象。 |
| 5 | public static string Concat(string str0, string str1, string str2, string str3) | 连接四个字符串对象。 |
| 6 | public bool Contains(string value) | 返回一个值,该值指示在此字符串中是否发生指定的String对象。 |
| 7 | public static string Copy(string str) | 创建与指定字符串相同值的新String对象。 |
| 8 | public void CopyTo(int sourceIndex, char[] destination, int destinationIndex, int count) | 将指定数量的字符从String对象的指定位置复制到Unicode字符数组中的指定位置。 |
| 9 | public bool EndsWith(string value) | 确定字符串对象的末尾是否与指定的字符串匹配。 |
| 10 | public bool Equals(string value) | 确定当前的String对象和指定的String对象是否具有相同的值。 |
| 11 | public static bool Equals(string a, string b) | 确定两个指定的String对象是否具有相同的值。 |
| 12 | public static string Format(string format, Object arg0) | 用指定对象的字符串表示替换指定字符串中的一个或多个格式项。 |
| 13 | public int IndexOf(char value) | 返回当前字符串中指定Unicode字符首次出现的从零开始的索引。 |
| 14 | public int IndexOf(string value) | 返回此实例中指定字符串第一次出现的从零开始的索引。 |
| 15 | public int IndexOf(char value, int startIndex) | 返回此字符串中指定Unicode字符第一次出现的从零开始的索引,在指定的字符位置开始搜索。 |
| 16 | public int IndexOf(string value, int startIndex) | 返回此实例中指定字符串第一次出现的从零开始的索引,在指定的字符位置开始搜索。 |
| 17 | public int IndexOfAny(char[] anyOf) | 返回指定Unicode字符数组中任何字符在此实例中第一次出现的基于零的索引。 |
| 18 | public int IndexOfAny(char[] anyOf, int startIndex) | 返回在指定的Unicode字符数组中的任何字符在这个实例的第一次出现的基于零的索引,在指定的字符位置开始搜索。 |
| 19 | public string Insert(int startIndex, string value) | 返回一个新的字符串,在当前字符串对象的指定索引位置插入指定的字符串。 |
| 20 | public static bool IsNullOrEmpty(string value) | 判断指定的字符串是空值还是空字符串。 |
| 21 | public static string Join(string separator, params string[] value) | 每个元素之间使用指定的分隔符来连接字符串数组的所有元素。 |
| 22 | public static string Join(string separator, string[] value, int startIndex, int count) | 每个元素之间使用指定的分隔符来连接字符串数组的指定元素。 |
| 23 | public int LastIndexOf(char value) | 返回当前字符串对象中指定Unicode字符,从零开始搜索的最后一次出现的索引位置。 |
| 24 | public int LastIndexOf(string value) | 返回当前字符串对象中指定字符串从零开始搜索的最后一次出现的索引位置。 |
| 25 | public string Remove(int startIndex) | 删除当前实例中从指定位置开始到最后一个位置的所有字符,并返回此字符串。 |
| 26 | public string Remove(int startIndex, int count) | 从指定位置移除当前字符串中指定数量的字符,并返回字符串。 |
| 27 | public string Replace(char oldChar, char newChar) | 使用指定的Unicode字符替换当前字符串对象中指定的Unicode字符的所有出现字符,并返回新的字符串。 |
| 28 | public string Replace(string oldValue, string newValue) | 使用指定的字符串替换当前字符串对象中指定字符串的所有出现字符,并返回新的字符串。 |
| 29 | public string[] Split(params char[] separator) | 返回一个字符串数组,其中包含当前字符串对象中的子字符串,由指定的Unicode字符数组的元素分隔。 |
| 30 | public string[] Split(char[] separator, int count) | 返回一个字符串数组,其中包含当前字符串对象中的子字符串,由指定的Unicode字符数组的元素分隔。 int参数指定要返回的子字符串的最大数量。 |
| 31 | public bool StartsWith(string value) | 确定此字符串实例的开始是否与指定的字符串匹配。 |
| 32 | public char[] ToCharArray() | 返回一个Unicode字符数组,其中包含当前字符串对象中的所有字符。 |
| 33 | public char[] ToCharArray(int startIndex, int length) | 返回一个Unicode字符数组,其中包含当前字符串对象中的所有字符(从指定的索引开始,直到指定的长度。) |
| 34 | public string ToLower() | 返回一个转换为小写的字符串的副本。 |
| 35 | public string ToUpper() | 返回一个转换为大写的字符串的副本。 |
| 36 | public string Trim() | 从当前String对象中删除所有前导和尾随的空格字符。 |
您可以访问MSDN库,获取方法和String类构造函数的完整列表。
实例
以下示例演示了上述一些方法:
比较字符串:
using System;
namespace StringApplication
{
class StringProg
{
static void Main(string[] args)
{
string str1 = "This is test";
string str2 = "This is text";
if (String.Compare(str1, str2) == 0)
{
Console.WriteLine(str1 + " and " + str2 + " are equal.");
}
else
{
Console.WriteLine(str1 + " and " + str2 + " are not equal.");
}
Console.ReadKey() ;
}
}
}
当编译和执行上述代码时,会产生以下结果:
This is test and This is text are not equal.
字符串包含字符串:
using System;
namespace StringApplication
{
class StringProg
{
static void Main(string[] args)
{
string str = "This is test";
if (str.Contains("test"))
{
Console.WriteLine("The sequence 'test' was found.");
}
Console.ReadKey() ;
}
}
}
当编译和执行上述代码时,会产生以下结果:
The sequence 'test' was found.
获取子字符串:
using System;
namespace StringApplication
{
class StringProg
{
static void Main(string[] args)
{
string str = "Last night I dreamt of Girls";
Console.WriteLine(str);
string substr = str.Substring(23);
Console.WriteLine(substr);
}
}
}
当编译和执行上述代码时,会产生以下结果:
Girls
将字符串连接:
using System;
namespace StringApplication
{
class StringProg
{
static void Main(string[] args)
{
string[] starray = new string[]{"Down the way nights are dark",
"And the sun shines daily on the mountain top",
"I took a trip on a sailing ship",
"And when I reached Jamaica",
"I made a stop"};
string str = String.Join("\n", starray);
Console.WriteLine(str);
}
}
}
当编译和执行上述代码时,会产生以下结果:
Down the way nights are dark
And the sun shines daily on the mountain top
I took a trip on a sailing ship
And when I reached Jamaica
I made a stop
结构体
在 C# 中,结构体是一种值类型数据类型。它可以帮助您使单个变量保存各种数据类型的相关数据在一起。struct关键字用于定义和创建一个结构。
结构体用于表示记录信息。假设您想在图书馆中跟踪记录书籍信息。假设希望跟踪每本书的以下属性:
- 标题
- 作者
- 学科
- 图书编号
定义结构
要定义一个结构,需要使用struct语句。struct语句定义了一个新的数据类型,有多个程序的成员。
例如,以下是声明Book结构体的方式:
struct Books
{
public string title;
public string author;
public string subject;
public int book_id;
};
以下示例程序显示如何使用结构体:
using System;
struct Books
{
public string title;
public string author;
public string subject;
public int book_id;
};
public class testStructure
{
public static void Main(string[] args)
{
Books Book1; /* Declare Book1 of type Book */
Books Book2; /* Declare Book2 of type Book */
/* book 1 specification */
Book1.title = "C Programming";
Book1.author = "Maxsu";
Book1.subject = "C Programming Tutorial";
Book1.book_id = 5493427;
/* book 2 specification */
Book2.title = "Telecom Billing";
Book2.author = "Sukida";
Book2.subject = "Telecom Billing Tutorial";
Book2.book_id = 8493480;
/* print Book1 info */
Console.WriteLine("Book 1 title : {0}", Book1.title);
Console.WriteLine("Book 1 author : {0}", Book1.author);
Console.WriteLine("Book 1 subject : {0}", Book1.subject);
Console.WriteLine("Book 1 book_id :{0}", Book1.book_id);
/* print Book2 info */
Console.WriteLine("Book 2 title : {0}", Book2.title);
Console.WriteLine("Book 2 author : {0}", Book2.author);
Console.WriteLine("Book 2 subject : {0}", Book2.subject);
Console.WriteLine("Book 2 book_id : {0}", Book2.book_id);
Console.ReadKey();
}
}
当编译和执行上述代码时,会产生以下结果:
Book 1 title : C Programming
Book 1 author : Maxsu
Book 1 subject : C Programming Tutorial
Book 1 book_id :5493427
Book 2 title : Telecom Billing
Book 2 author : Sukida
Book 2 subject : Telecom Billing Tutorial
Book 2 book_id : 8493480
C# 结构体的特点
在上面地示例中,我们已经使用了一个名为Books的简单结构体。 C# 中的结构与传统的C语言或C++中的结构完全不同。 C# 结构具有以下特点:
- 结构体可以有方法,字段,索引器,属性,操作符方法和事件。
- 结构体可以有定义的构造函数,但不能是析构函数。但是不能为结构定义默认构造函数。默认构造函数是自动定义的,不能被更改。
- 与类不同,结构体不能继承其他结构体或类。
- 结构体不能用作其他结构或类的基础。
- 结构体可以实现一个或多个接口。
- 结构成员不能被指定为抽象,虚拟或受保护。
- 当使用
New运算符创建一个struct对象时,它将调用相应的构造函数。 与类不同,可以在不使用New运算符的情况下实例化结构体。 - 如果不使用
New运算符,则字段保持未分配,并且在所有字段初始化之前不能使用对象。
类与结构区别
类和结构有以下基本差别:
- 类是引用类型,结构体是值类型
- 结构体不支持继承
- 结构体不能有默认构造函数
根据上述讨论,下面我们来重写上一个例子:
using System;
struct Books
{
private string title;
private string author;
private string subject;
private int book_id;
public void getValues(string t, string a, string s, int id)
{
title = t;
author = a;
subject = s;
book_id = id;
}
public void display()
{
Console.WriteLine("Title : {0}", title);
Console.WriteLine("Author : {0}", author);
Console.WriteLine("Subject : {0}", subject);
Console.WriteLine("Book_id :{0}", book_id);
}
};
public class testStructure
{
public static void Main(string[] args)
{
Books Book1 = new Books(); /* Declare Book1 of type Book */
Books Book2 = new Books(); /* Declare Book2 of type Book */
/* book 1 specification */
Book1.getValues("C Programming",
"Maxsu", "C Programming Tutorial", 749540712);
/* book 2 specification */
Book2.getValues("Telecom Billing",
"Sukida", "Telecom Billing Tutorial", 59570011);
/* print Book1 info */
Book1.display();
/* print Book2 info */
Book2.display();
Console.ReadKey();
}
}
当编译和执行上述代码时,会产生以下结果:
Title : C Programming
Author : Maxsu
Subject : C Programming Tutorial
Book_id :749540712
Title : Telecom Billing
Author : Sukida
Subject : Telecom Billing Tutorial
Book_id :59570011
枚举
枚举(enum)是一组命名的整数常量。枚举类型是使用enum关键字来声明。
C# 枚举是值数据类型。 换句话说,枚举包含其自己的值,并且不能继承或不能传递继承。
声明枚举变量
声明枚举的一般语法是:
enum <enum_name>
{
enumeration list
};
其中,
- enum_name - 指定枚举类型名称。
- enumeration list - 是使用逗号分隔的标识符列表。
枚举列表中的每个符号表示一个整数值,后面符号的值大于前面符号的值。 默认情况下,第一个枚举符号的值为0,例如:
enum Days { Sun, Mon, tue, Wed, thu, Fri, Sat };
示例
以下示例演示了如何使用枚举变量:
using System;
namespace EnumApplication
{
class EnumProgram
{
enum Days { Sun, Mon, tue, Wed, thu, Fri, Sat };
static void Main(string[] args)
{
int WeekdayStart = (int)Days.Mon;
int WeekdayEnd = (int)Days.Fri;
Console.WriteLine("Monday: {0}", WeekdayStart);
Console.WriteLine("Friday: {0}", WeekdayEnd);
Console.ReadKey();
}
}
}
当编译和执行上述代码时,会产生以下结果:
Monday: 1
Friday: 5
更改开始索引示例
using System;
public class EnumExample
{
public enum Season { WINTER=10, SPRING, SUMMER, FALL }
public static void Main()
{
int x = (int)Season.WINTER;
int y = (int)Season.SUMMER;
Console.WriteLine("WINTER = {0}", x);
Console.WriteLine("SUMMER = {0}", y);
}
}
当编译和执行上述代码时,会产生以下结果:
WINTER = 10
SUMMER = 12
枚举示例:使用getNames()遍历所有值
using System;
public class EnumExample
{
public enum Days { Sun, Mon, Tue, Wed, Thu, Fri, Sat };
public static void Main()
{
foreach (string s in Enum.GetNames(typeof(Days)))
{
Console.WriteLine(s);
}
}
}
当编译和执行上述代码时,会产生以下结果:
Sun
Mon
Tue
Wed
Thu
Fri
Sat
枚举示例:使用getValues()遍历所有值
using System;
public class EnumExample
{
public enum Days { Sun, Mon, Tue, Wed, Thu, Fri, Sat };
public static void Main()
{
foreach (Days d in Enum.GetValues(typeof(Days)))
{
Console.WriteLine(d);
}
}
}
当编译和执行上述代码时,会产生以下结果:
Sun
Mon
Tue
Wed
Thu
Fri
Sat
类
类是对象的蓝图或模板,可以定义类来表示某种数据类型。这实际上并不定义任何数据,但它确实定义了类名称的含义。也就是说,该类的对象由哪个对象组成,哪些对象可以执行什么操作。 对象是类的实例。 构成类的方法和变量称为类的成员。
定义一个类
类定义从class关键字开始,后跟类名称; 和由一对花括号括在一起表示类的主体。 以下是类定义的一般形式:
<access specifier> class class_name
{
// member variables
<access specifier> <data type> variable1;
<access specifier> <data type> variable2;
...
<access specifier> <data type> variableN;
// member methods
<access specifier> <return type> method1(parameter_list)
{
// method1 body
}
<access specifier> <return type> method2(parameter_list)
{
// method2 body
}
...
<access specifier> <return type> methodN(parameter_list)
{
// method3 body
}
}
注意:
- 访问说明符(access specifier)指定成员以及类本身的访问规则。如果没有指定,则类的默认访问说明符是
internal。成员的默认访问权限是private。 - 数据类型(data type)指定变量的类型,返回类型(return type)指定方法返回的数据的数据类型(如果有)。
- 要访问类成员,请使用点(
.)运算符。 - 点(
.)运算符将对象的名称与成员的名称相链接。
以下示例说明了上面所讨论的概念:
using System;
namespace BoxApplication
{
class Box
{
public double length; // Length of a box
public double breadth; // Breadth of a box
public double height; // Height of a box
}
class Boxtester
{
static void Main(string[] args)
{
Box Box1 = new Box(); // Declare Box1 of type Box
Box Box2 = new Box(); // Declare Box2 of type Box
double volume = 0.0; // Store the volume of a box here
// box 1 specification
Box1.height = 5.0;
Box1.length = 6.0;
Box1.breadth = 7.0;
// box 2 specification
Box2.height = 10.0;
Box2.length = 12.0;
Box2.breadth = 13.0;
// volume of box 1
volume = Box1.height * Box1.length * Box1.breadth;
Console.WriteLine("Volume of Box1 : {0}", volume);
// volume of box 2
volume = Box2.height * Box2.length * Box2.breadth;
Console.WriteLine("Volume of Box2 : {0}", volume);
Console.ReadKey();
}
}
}
当编译和执行上述代码时,会产生以下结果:
Volume of Box1 : 210
Volume of Box2 : 1560
成员函数和封装
类的成员函数是在类中定义具有与其他变量类似的定义或其原型的函数。它对其所属类的任何对象进行操作,并且可以访问该对象的类的所有成员。
成员变量是对象的属性(从设计的角度),它们被保留为私有(private)以实现封装。这些变量只能使用公共成员函数来访问。
为了理解上面的概念,我们从一个类中设置并读取另外一个类的成员的值:
using System;
namespace BoxApplication
{
class Box
{
private double length; // Length of a box
private double breadth; // Breadth of a box
private double height; // Height of a box
public void setLength( double len )
{
length = len;
}
public void setBreadth( double bre )
{
breadth = bre;
}
public void setHeight( double hei )
{
height = hei;
}
public double getVolume()
{
return length * breadth * height;
}
}
class Boxtester
{
static void Main(string[] args)
{
Box Box1 = new Box(); // Declare Box1 of type Box
Box Box2 = new Box();
double volume;
// Declare Box2 of type Box
// box 1 specification
Box1.setLength(6.0);
Box1.setBreadth(7.0);
Box1.setHeight(5.0);
// box 2 specification
Box2.setLength(12.0);
Box2.setBreadth(13.0);
Box2.setHeight(10.0);
// volume of box 1
volume = Box1.getVolume();
Console.WriteLine("Volume of Box1 : {0}" ,volume);
// volume of box 2
volume = Box2.getVolume();
Console.WriteLine("Volume of Box2 : {0}", volume);
Console.ReadKey();
}
}
}
当编译和执行上述代码时,会产生以下结果:
Volume of Box1 : 210
Volume of Box2 : 1560
C# 构造函数
类构造函数是当创建该类的新对象时执行的一个类的特殊成员函数。
构造函数具有与类完全相同的名称,它没有任何返回值。下面的例子解释了构造函数的概念:
using System;
namespace LineApplication
{
class Line
{
private double length; // Length of a line
public Line()
{
Console.WriteLine("Object is being created");
}
public void setLength( double len )
{
length = len;
}
public double getLength()
{
return length;
}
static void Main(string[] args)
{
Line line = new Line();
// set line length
line.setLength(6.0);
Console.WriteLine("Length of line : {0}", line.getLength());
Console.ReadKey();
}
}
}
当编译和执行上述代码时,会产生以下结果:
Object is being created
Length of line : 6
默认构造函数没有任何参数,但是如果需要,构造函数可以有参数。这样的构造函数被称为参数化构造函数。此技术可用在创建时为对象分配初始值,如以下示例所示:
using System;
namespace LineApplication
{
class Line
{
private double length; // Length of a line
public Line(double len) //Parameterized constructor
{
Console.WriteLine("Object is being created, length = {0}", len);
length = len;
}
public void setLength( double len )
{
length = len;
}
public double getLength()
{
return length;
}
static void Main(string[] args)
{
Line line = new Line(100.0);
Console.WriteLine("Length of line : {0}", line.getLength());
// set line length
line.setLength(60.0);
Console.WriteLine("Length of line : {0}", line.getLength());
Console.ReadKey();
}
}
}
当编译和执行上述代码时,会产生以下结果:
Object is being created, length = 100
Length of line : 100
Length of line : 60
C# 析构函数
析构函数是一个类的特殊成员函数,只要其类的对象超出范围就执行。析构函数使用具有前缀波形符(~)和类名称来表示,并且它不能拥有返回值,也不能使用任何参数。
析构函数在退出程序之前释放内存资源非常有用。 析构函数不能被继承或重载。
以下示例解释析构函数的用法,参考代码:
using System;
namespace LineApplication
{
class Line
{
private double length; // Length of a line
public Line() // constructor
{
Console.WriteLine("Object is being created");
}
~Line() //destructor
{
Console.WriteLine("Object is being deleted");
}
public void setLength( double len )
{
length = len;
}
public double getLength()
{
return length;
}
static void Main(string[] args)
{
Line line = new Line();
// set line length
line.setLength(126.0);
Console.WriteLine("Length of line : {0}", line.getLength());
}
}
}
当编译和执行上述代码时,会产生以下结果:
Object is being created
Length of line : 126
Object is being deleted
C# 类静态成员
我们可以使用static关键字将类成员定义为静态(static)。 当将一个类的成员声明为静态时,则无论创建了多少个类的对象,静态成员只有一个副本。
在一个类使用关键字static来修辞成员,则表示实例只有一个成员存在。静态变量用于定义常量,因为它们的值可以通过调用该类而不创建它的实例来引用。 静态变量可以在成员函数或类定义之外初始化。还可以在类定义内初始化静态变量。
以下示例演示了如何使用静态变量:
using System;
namespace StaticVarApplication
{
class StaticVar
{
public static int num;
public void count()
{
num++;
}
public int getNum()
{
return num;
}
}
class StaticTester
{
static void Main(string[] args)
{
StaticVar s1 = new StaticVar();
StaticVar s2 = new StaticVar();
s1.count();
s1.count();
s1.count();
s2.count();
Console.WriteLine("Variable num for s1: {0}", s1.getNum());
Console.WriteLine("Variable num for s2: {0}", s2.getNum());
Console.ReadKey();
}
}
}
当编译和执行上述代码时,会产生以下结果:
Variable num for s1: 4
Variable num for s2: 4
您也可以将成员函数声明为静态。这些函数只能访问静态变量。静态函数即使在创建对象之前也存在。以下示例演示了如何使用静态函数:
using System;
namespace StaticVarApplication
{
class StaticVar
{
public static int num;
public void count()
{
num++;
}
public static int getNum()
{
return num;
}
}
class StaticTester
{
static void Main(string[] args)
{
StaticVar s = new StaticVar();
s.count();
s.count();
s.count();
Console.WriteLine("Variable num: {0}", StaticVar.getNum());
Console.ReadKey();
}
}
}
当编译和执行上述代码时,会产生以下结果:
Variable num: 3
继承
面向对象编程中最重要的概念之一是继承。继承允许根据另一个类定义一个类,这样可以更容易地创建和维护应用程序。这也提供了重用代码并加快了代码的实现。
当创建一个类时,程序员可以指定这个新类应该继承现有类的哪些成员,而不是编写继承全部的数据成员和成员函数。这个现有的类称为基类,新类称为派生类或子类。
继承的想法实现了IS-A(是一个什么类?)的关系。 例如,哺乳动物IS是动物,狗IS-A哺乳动物,因此也是狗IS-A动物,等等。
基类和派生类
类可以从多个类或接口派生,它可以从多个基类或接口继承数据和函数。
C# 中创建派生类,可使用如下语法:
<acess-specifier> class <base_class>
{
...
}
class <derived_class> : <base_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;
}
// Derived class
class Rectangle: Shape
{
public int getArea()
{
return (width * height);
}
}
class RectangleTester
{
static void Main(string[] args)
{
Rectangle Rect = new Rectangle();
Rect.setWidth(10);
Rect.setHeight(20);
// Print the area of the object.
Console.WriteLine("Total area: {0}", Rect.getArea());
Console.ReadKey();
}
}
}
当编译和执行上述代码时,会产生以下结果:
Total area: 200
初始化基类
派生类继承基类成员变量和成员方法。 因此,在创建子类之前应该创建超类对象。您可以在成员初始化列表中给出超类初始化的说明或具体实现。
以下程序演示如何实现:
using System;
namespace RectangleApplication
{
class Rectangle
{
//member variables
protected double length;
protected double width;
public Rectangle(double l, double w)
{
length = l;
width = w;
}
public double GetArea()
{
return length * width;
}
public void Display()
{
Console.WriteLine("Length: {0}", length);
Console.WriteLine("Width: {0}", width);
Console.WriteLine("Area: {0}", GetArea());
}
}//end class Rectangle
class Tabletop : Rectangle
{
private double cost;
public Tabletop(double l, double w) : base(l, w)
{ }
public double GetCost()
{
double cost;
cost = GetArea() * 70;
return cost;
}
public void Display()
{
base.Display();
Console.WriteLine("Cost: {0}", GetCost());
}
}
class ExecuteRectangle
{
static void Main(string[] args)
{
Tabletop t = new Tabletop(4.5, 7.5);
t.Display();
Console.ReadLine();
}
}
}
当编译和执行上述代码时,会产生以下结果:
Length: 4.5
Width: 7.5
Area: 33.75
Cost: 2362.5
C# 多重继承
C# 不支持多重继承。但是,可以使用接口来实现多重继承。下面示例程序将演示如何实现:
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;
}
// Base class PaintCost
public interface PaintCost
{
int getCost(int area);
}
// Derived class
class Rectangle : Shape, PaintCost
{
public int getArea()
{
return (width * height);
}
public int getCost(int area)
{
return area * 70;
}
}
class RectangleTester
{
static void Main(string[] args)
{
Rectangle Rect = new Rectangle();
int area;
Rect.setWidth(5);
Rect.setHeight(7);
area = Rect.getArea();
// Print the area of the object.
Console.WriteLine("Total area: {0}", Rect.getArea());
Console.WriteLine("Total paint cost: ${0}" , Rect.getCost(area));
Console.ReadKey();
}
}
}
当编译和执行上述代码时,会产生以下结果:
运算符重载
在C#中,可以重新定义或重载大多数内置运算符。 因此,程序员也可以使用具有用户定义类型的运算符。重载运算符是具有特殊名称的功能,关键字operator后跟定义运算符的符号。 类似于任何其他函数定义,重载运算符具有返回类型和参数列表。
例如,通过以下示例函数:
public static Box operator+ (Box b, Box c)
{
Box box = new Box();
box.length = b.length + c.length;
box.breadth = b.breadth + c.breadth;
box.height = b.height + c.height;
return box;
}
上述函数为用户自定义Box类实现加法运算符(+)。它实现相加两个Box对象的属性,并返回生成的Box对象。
实现操作符重载
以下程序显示了完整的实现:
using System;
namespace OperatorOvlApplication
{
class Box
{
private double length; // Length of a box
private double breadth; // Breadth of a box
private double height; // Height of a box
public double getVolume()
{
return length * breadth * height;
}
public void setLength(double len)
{
length = len;
}
public void setBreadth(double bre)
{
breadth = bre;
}
public void setHeight(double hei)
{
height = hei;
}
// Overload + operator to add two Box objects.
public static Box operator +(Box b, Box c)
{
Box box = new Box();
box.length = b.length + c.length;
box.breadth = b.breadth + c.breadth;
box.height = b.height + c.height;
return box;
}
}
class Tester
{
static void Main(string[] args)
{
Box Box1 = new Box(); // Declare Box1 of type Box
Box Box2 = new Box(); // Declare Box2 of type Box
Box Box3 = new Box(); // Declare Box3 of type Box
double volume = 0.0; // Store the volume of a box here
// box 1 specification
Box1.setLength(60.0);
Box1.setBreadth(70.0);
Box1.setHeight(50.0);
// box 2 specification
Box2.setLength(121.0);
Box2.setBreadth(133.0);
Box2.setHeight(110.0);
// volume of box 1
volume = Box1.getVolume();
Console.WriteLine("Volume of Box1 : {0}", volume);
// volume of box 2
volume = Box2.getVolume();
Console.WriteLine("Volume of Box2 : {0}", volume);
// Add two object as follows:
Box3 = Box1 + Box2;
// volume of box 3
volume = Box3.getVolume();
Console.WriteLine("Volume of Box3 : {0}", volume);
Console.ReadKey();
}
}
}
当编译和执行上述代码时,会产生以下结果:
Volume of Box1 : 210000
Volume of Box2 : 1770230
Volume of Box3 : 5878880
可重载和不可重载的运算符
下表描述了 C# 中重载的运算符:
| 操作符 | 描述 |
|---|---|
+, -, !, ~, ++, -- |
这些一元运算符需要一个操作数,并且可以重载。 |
+, -, *, /, % |
这些二进制运算符取一个操作数并且可以重载。 |
==, !=, <, >, <=, >= |
这些比较运算符都可以重载 |
&&, // |
条件逻辑运算符不能直接重载。 |
+=, -=,*=,/=,%= |
赋值运算符不能被重载。 |
=, ., ?:, ->, new, is, sizeof, typeof |
这些运算符不能重载。 |
示例
根据上述讨论,我们来扩展上面的例子,并且重载更多的运算符:
using System;
namespace OperatorOvlApplication
{
class Box
{
private double length; // Length of a box
private double breadth; // Breadth of a box
private double height; // Height of a box
public double getVolume()
{
return length * breadth * height;
}
public void setLength( double len )
{
length = len;
}
public void setBreadth( double bre )
{
breadth = bre;
}
public void setHeight( double hei )
{
height = hei;
}
// Overload + operator to add two Box objects.
public static Box operator+ (Box b, Box c)
{
Box box = new Box();
box.length = b.length + c.length;
box.breadth = b.breadth + c.breadth;
box.height = b.height + c.height;
return box;
}
public static bool operator == (Box lhs, Box rhs)
{
bool status = false;
if (lhs.length == rhs.length && lhs.height == rhs.height && lhs.breadth == rhs.breadth)
{
status = true;
}
return status;
}
public static bool operator !=(Box lhs, Box rhs)
{
bool status = false;
if (lhs.length != rhs.length || lhs.height != rhs.height || lhs.breadth != rhs.breadth)
{
status = true;
}
return status;
}
public static bool operator <(Box lhs, Box rhs)
{
bool status = false;
if (lhs.length < rhs.length && lhs.height < rhs.height && lhs.breadth < rhs.breadth)
{
status = true;
}
return status;
}
public static bool operator >(Box lhs, Box rhs)
{
bool status = false;
if (lhs.length > rhs.length && lhs.height > rhs.height && lhs.breadth > rhs.breadth)
{
status = true;
}
return status;
}
public static bool operator <=(Box lhs, Box rhs)
{
bool status = false;
if (lhs.length <= rhs.length && lhs.height <= rhs.height && lhs.breadth <= rhs.breadth)
{
status = true;
}
return status;
}
public static bool operator >=(Box lhs, Box rhs)
{
bool status = false;
if (lhs.length >= rhs.length && lhs.height >= rhs.height && lhs.breadth >= rhs.breadth)
{
status = true;
}
return status;
}
public override string ToString()
{
return String.Format("({0}, {1}, {2})", length, breadth, height);
}
}
class Tester
{
static void Main(string[] args)
{
Box Box1 = new Box(); // Declare Box1 of type Box
Box Box2 = new Box(); // Declare Box2 of type Box
Box Box3 = new Box(); // Declare Box3 of type Box
Box Box4 = new Box();
double volume = 0.0; // Store the volume of a box here
// box 1 specification
Box1.setLength(6.0);
Box1.setBreadth(7.0);
Box1.setHeight(5.0);
// box 2 specification
Box2.setLength(12.0);
Box2.setBreadth(13.0);
Box2.setHeight(10.0);
//displaying the Boxes using the overloaded ToString():
Console.WriteLine("Box 1: {0}", Box1.ToString());
Console.WriteLine("Box 2: {0}", Box2.ToString());
// volume of box 1
volume = Box1.getVolume();
Console.WriteLine("Volume of Box1 : {0}", volume);
// volume of box 2
volume = Box2.getVolume();
Console.WriteLine("Volume of Box2 : {0}", volume);
// Add two object as follows:
Box3 = Box1 + Box2;
Console.WriteLine("Box 3: {0}", Box3.ToString());
// volume of box 3
volume = Box3.getVolume();
Console.WriteLine("Volume of Box3 : {0}", volume);
//comparing the boxes
if (Box1 > Box2)
Console.WriteLine("Box1 is greater than Box2");
else
Console.WriteLine("Box1 is greater than Box2");
if (Box1 < Box2)
Console.WriteLine("Box1 is less than Box2");
else
Console.WriteLine("Box1 is not less than Box2");
if (Box1 >= Box2)
Console.WriteLine("Box1 is greater or equal to Box2");
else
Console.WriteLine("Box1 is not greater or equal to Box2");
if (Box1 <= Box2)
Console.WriteLine("Box1 is less or equal to Box2");
else
Console.WriteLine("Box1 is not less or equal to Box2");
if (Box1 != Box2)
Console.WriteLine("Box1 is not equal to Box2");
else
Console.WriteLine("Box1 is not greater or equal to Box2");
Box4 = Box3;
if (Box3 == Box4)
Console.WriteLine("Box3 is equal to Box4");
else
Console.WriteLine("Box3 is not equal to Box4");
Console.ReadKey();
}
}
}
当编译和执行上述代码时,会产生以下结果:
Box 1: (6, 7, 5)
Box 2: (12, 13, 10)
Volume of Box1 : 210
Volume of Box2 : 1560
Box 3: (18, 20, 15)
Volume of Box3 : 5400
Box1 is not greater than Box2
Box1 is less than Box2
Box1 is not greater or equal to Box2
Box1 is less or equal to Box2
Box1 is not equal to Box2
Box3 is equal to Box4
接口
接口是一种为所有继承接口的类定义需要遵循的语法约定。接口定义了语法约定的“是什么”部分,派生类定义了语法约定的“如何实现”部分。
接口定义属性,方法和事件,它们是接口的成员。 接口只包含成员的声明。 派生类负责定义和实现成员。接口通常有助于为派生类提供遵循的标准结构。
抽象类在某种程度上起着相同的作用,但是,当基类只有少数方法声明并且由派生类实现时,才会考虑使用抽象类。
声明接口
接口使用interface关键字声明。 它类似于类声明。 默认情况下,interface语句是公开的。 以下是接口声明的示例:
public interface ITransactions
{
// interface members
void showTransaction();
double getAmount();
}
示例
以下示例演示了以上所述接口的实现:
using System.Collections.Generic;
using System.Linq;
using System.Text;
using System;
namespace InterfaceApplication
{
public interface ITransactions
{
// interface members
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("1001", "8/10/2017", 578998.00);
Transaction t2 = new Transaction("1002", "9/10/2018", 5471980.00);
t1.showTransaction();
t2.showTransaction();
Console.ReadKey();
}
}
}
当编译和执行上述代码时,会产生以下结果:
Transaction: 1001
Date: 8/10/2017
Amount: 578998
Transaction: 1002
Date: 9/10/2018
Amount: 5471980
命名空间
命名空间旨在提供一种将一组名称与另一组名称分开的方法。在一个命名空间中声明的类名称与在另一个命名空间中声明的相同类名称不冲突。
定义命名空间
命名空间定义以关键字namespace开头,后跟命名空间名称如下:
namespace namespace_name
{
// code declarations
}
要调用命名空间启用的任一函数或变量的版本,请按如下所示使用命名空间名称指定:
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类是在System命名空间中定义的,所以只简写为:
Console.WriteLine ("Hello there");
可以把完整的名称写成:
System.Console.WriteLine("Hello there");
还可以使用using namespace伪指令避免前缀命名空间。该指令告诉编译器后续代码正在使用指定命名空间中的名称。因此,命名空间隐含在以下代码中:
下面重写我们重写前面的例子,使用以下代码中的指令:
using System;
using first_space;
using second_space;
namespace first_space
{
class abc
{
public void func()
{
Console.WriteLine("Inside first_space");
}
}
}
namespace second_space
{
class efg
{
public void func()
{
Console.WriteLine("Inside second_space");
}
}
}
class TestClass
{
static void Main(string[] args)
{
abc fc = new abc();
efg sc = new efg();
fc.func();
sc.func();
Console.ReadKey();
}
}
当编译和执行上述代码时,会产生以下结果:
Inside first_space
Inside second_space
嵌套命名空间
可以在一个命名空间中定义另一个命名空间,如下所示:
namespace namespace_name1
{
// code declarations
namespace namespace_name2
{
// code declarations
}
}
可以使用点(.)运算符访问嵌套命名空间的成员,如下所示:
using System;
using first_space;
using first_space.second_space;
namespace first_space
{
class abc
{
public void func()
{
Console.WriteLine("Inside first_space");
}
}
namespace second_space
{
class efg
{
public void func()
{
Console.WriteLine("Inside second_space");
}
}
}
}
class TestClass
{
static void Main(string[] args)
{
abc fc = new abc();
efg sc = new efg();
fc.func();
sc.func();
Console.ReadKey();
}
}
当编译和执行上述代码时,会产生以下结果:
Inside first_space
Inside second_space
预处理指令
预处理器指令用来在编译开始之前给编译器指示预处理信息。
所有预处理程序指令以符号#开头,并且只有空格字符在一行上,空格符可预处理器指令之前出现。 预处理器指令不是语句,所以它们不以分号(;)结尾。
C# 编译器没有分隔预处理器;,指令被处理为只有一个。 在 C# 中,预处理指令用于指示条件编译。与C语言和C++指令不同,它们不用于创建宏。 预处理程序指令必须是一行中的唯一指令。
C# 预处理器指令
下表列出了 C# 中提供的预处理指令:
| 预处理指令 | 描述 |
|---|---|
#define |
定义一个叫作符号的字符序列。 |
#undef |
用于取消定义的符号。 |
#if |
用来测试符号以查看它们是否为真。 |
#else |
用来与#if来创建一个复合条件指令。 |
#elif |
用来创建一个复合条件指令。 |
#endif |
指定条件指令的结束。 |
#line |
用于修改编译器的行号和(可选)修改错误和警告的文件名输出。 |
#error |
用于从代码中的特定位置生成错误。 |
#warning |
用于从代码中的特定位置生成一级警告。 |
#region |
允许您在使用Visual Studio代码编辑器的概述功能时指定可以展开或折叠的代码块。 |
#endregion |
用于标志着#region块的结束。 |
#define预处理程序
#define预处理程序指令创建符号常量。
#define允许定义一个符号,通过使用该符号作为传递给#if指令的表达式,表达式的计算结果为true。其语法如下:
#define symbol
以下程序演示如何使用#define指令:
#define PI
using System;
namespace PreprocessorDAppl
{
class Program
{
static void Main(string[] args)
{
#if (PI)
Console.WriteLine("PI is defined");
#else
Console.WriteLine("PI is not defined");
#endif
Console.ReadKey();
}
}
}
当编译和执行上述代码时,会产生以下结果:
PI is defined
条件指令
可以使用#if指令创建条件指令。条件指令用于测试符号检查它们的值是否为真。 如果它们评估求值为true,编译器将评估求值#if到下一个指令之间的所有代码。
条件指令的语法是:
#if symbol [operator symbol]...
其中,符号(symbol)是要测试的符号的名称。也可以使用布尔值:true和false,或者使用否定运算符加到符号的前面。
运算符符号是用于评估符号的运算符。运算符可以是以下任一项:
==(相等)!=(不等式)&&(和)||(或)
也可以使用括号对符号和运算符进行分组。在用于编译调试版本的代码或编译特定配置时可使用条件指令。 以#if指令开头的条件指令必须明确地使用#endif指令终止。
以下程序演示了条件指令的用法:
#define DEBUG
#define VC_V10
using System;
public class TestClass
{
public static void Main()
{
#if (DEBUG && !VC_V10)
Console.WriteLine("DEBUG is defined");
#elif (!DEBUG && VC_V10)
Console.WriteLine("VC_V10 is defined");
#elif (DEBUG && VC_V10)
Console.WriteLine("DEBUG and VC_V10 are defined");
#else
Console.WriteLine("DEBUG and VC_V10 are not defined");
#endif
Console.ReadKey();
}
}
当编译和执行上述代码时,会产生以下结果:
DEBUG and VC_V10 are defined
其他结构
正则表达式是匹配输入文本的模式。.Net框架提供了允许这种匹配的正则表达式引擎。模式由一个或多个字符文字,运算符或构造组成。
定义正则表达式的构造
有各种类型的字符,运算符和结构,可以让您用来定义正则表达式。点击以下链接查找这些结构。
- 字符转义
- 字符类
- 锚(Anchors)
- 分组结构
- 限定符
- 反向引用结构
- 交替构造
- 替换
- 其他结构
字符转义
这里介绍的基本上是特殊字符或转义字符。正则表达式中的反斜杠字符(\)表示其后面的字符是特殊字符,也可以在字面上进行解释。
下表列出了转义字符:
| 转义字符 | 描述 | 模式 | 匹配 |
|---|---|---|---|
\a |
匹配一个响铃字符\u0007。 |
\a |
"\u0007"匹配"Warning!" + '\u0007' |
\b |
在一个字符类中,匹配一个退格\u0008。 |
[\b]{3,} |
"\b\b\b\b" 匹配"\b\b\b\b" |
\t |
匹配一个制表符,即:\u0009。 |
(\w+)\t |
"Name\t", "Addr\t" 匹配"Name\tAddr\t" |
\r |
匹配回车(\u000D),(\r不等同于换行符\n) |
\r\n(\w+) |
"\r\nHello"匹配"\r\Hello\nWorld." |
\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\nHello"匹配 "\r\Hello\nWorld." |
\e |
匹配转义,\u001B。 |
\e |
"\x001B" 匹配 "\x001B" |
\nnn |
使用八进制表示来指定一个字符(nnn由三位数组成)。 |
\w\040\w |
"a b", "c d"匹配 "a bc d" |
\x nn |
使用十六进制表示来指定一个字符(nn由两位数组成)。 |
\w\x20\w |
"a b", "c d"匹配 "a bc d" |
\c X\c x |
匹配由X或x指定的ASCII控制字符,其中X或x是控制字符的字母。 |
\cC |
“\x0003”匹配“\x0003”(Ctrl-C) |
\u nnnn |
通过使用十六进制表示(正好四位数,由nnnn表示)匹配Unicode字符。 |
\w\u0020\w |
"a b", "c d"匹配"a bc d" |
\ |
当后跟一个不被识别为转义字符的字符时,匹配该字符。 | \d+[\+-x\*]\d+\d+[\+-x\*\d+ |
"2+2" 和 "3*9" 匹配 "(2+2) * 3*9" |
字符类
一个字符类匹配一组字符中的任何一个,下表描述了字符类:
| 字符类 | 描述 | 模式 | 匹配 |
|---|---|---|---|
[character_group] |
匹配character_group中的任何单个字符。 默认情况下,匹配区分大小写。 |
[mn] |
"m"可匹配"mat" "m", "n"可匹配"moon" |
[^character_group] |
否定:匹配任何不在character_group中的单个字符。 默认情况下,字符incharacter_group区分大小写。 |
[^aei] |
"v", "l" 可匹配 "avail" |
[ first - last ] |
字符范围:匹配从第一个到最后一个范围内的任何单个字符。 | [b-d] |
[b-d]irds匹配Birds, Cirds, Dirds |
. |
通配符:匹配\n以外的任何单个字符。 |
a.e |
"ave"匹配"have" ,"ate"匹配"mate" |
\p{ name } |
匹配Unicode通用类别中的任何单个字符或名称指定的命名块。 | \p{Lu} |
"C", "L"匹配"City Lights" |
\P{ name } |
匹配不在Unicode通用类别中的任何单个字符或由名称指定的命名块。 | \P{Lu} |
"i", "t", "y" 匹配"City" |
\w |
匹配任何字符 | \w |
"R", "o", "m" 以及 "1"匹配"Room#1" |
\W |
匹配任何非字词 | \W |
"#"匹配"Room#1" |
\s |
匹配任何空白字符 | \w\s |
"D "匹配"ID A1.3" |
\S |
匹配任何非空格字符 | \s\S |
" _"匹配"int __ctr" |
\d |
匹配任何十进制数字。 | \d |
"4"匹配"4 = IV" |
\D |
匹配十进制数字以外的任何字符。 | \D |
" ", "=", " ", "I", "V"匹配"4 = IV" |
锚(Anchors)
根据字符串中的当前位置,锚点允许匹配成功或失败。下表列出了锚点:
| 断言 | 描述 | 模式 | 匹配 |
|---|---|---|---|
^ |
匹配必须从字符串或行的开头开始。 | ^\d{3} |
"567" 匹配 "567-777-" |
$ |
匹配必须发生在字符串的末尾或\n之前的行或字符串的末尾。 |
-\d{4}$ |
"-2012"匹配"8-12-2012" |
\A |
匹配必须在字符串的开头出现。 | \A\w{3} |
"Code"匹配"Code-007-" |
\Z |
匹配必须在字符串的末尾或字符串末尾的\n之前出现 |
-\d{3}\Z |
"-007"匹配"Bond-901-007" |
\z |
匹配必须在字符串的末尾 | -\d{3}\z |
"-333"匹配"-901-333" |
\G |
匹配必须在上一个匹配结束时发生。 | \\G\(\d\) |
"(1)", "(3)", "(5)"匹配"(1)(3)(5)[7](9)" |
\b |
匹配必须发生在\w(字母数字)和\W(非字母数字)字符之间的边界上。 |
\w |
"R", "o", "m","1"匹配"Room#1" |
\B |
匹配不得发生在\b边界上。 |
\Bend\w*\b |
"ends","ender"匹配"end sends endure lender" |
分组结构
分组构造描述正则表达式的子表达式并捕获输入字符串的子字符串。下表列出了分组结构:
| 分组结构 | 描述 | 模式 | 匹配 |
|---|---|---|---|
( subexpression ) |
捕获匹配的子表达式,并将其分配为从零开始的序数 | (\w)\1 |
"ee"匹配"deep" |
(?< name >subexpression) |
将匹配的子表达式捕获到命名组中 | (?< double>\w)\k< double> |
"ee"匹配"deep" |
(?< name1 -name2 >subexpression) |
定义平衡组定义 | (((?'Open'\()[^\(\)]*)+((?'Close-Open'\))[^\(\)]*)+)*(?(Open)(?!))$ |
"((1-3)*(3-1))"匹配"3+2^((1-3)*(3-1))" |
(?: subexpression) |
定义非捕获组 | Write(?:Line)? |
"WriteLine" 匹配"Console.WriteLine()" |
(?imnsx-imnsx:subexpression) |
在子表达式中应用或禁用指定的选项 | A\d{2}(?i:\w+)\b |
"A12xl", "A12XL" 匹配 "A12xl A12XL a12xl" |
(?= subexpression) |
零宽正向前瞻断言 | \w+(?=\.) |
"is", "ran", 和 "out" 匹配 "He is. The dog ran. The sun is out." |
(?! subexpression) |
零宽度负向前瞻断言 | \b(?!un)\w+\b |
"sure", "used"匹配 "unsure sure unity used" |
(?< =subexpression) |
零宽度正向后断言 | (?< =19)\d{2}\b |
"51", "03" 匹配 "1851 1999 1950 1905 2003" |
(?< ! subexpression) |
零宽度负向后断言 | (?< !19)\d{2}\b |
"ends", "ender"匹配 "end sends endure lender" |
(?> subexpression) |
非追溯(或“贪懒”)子表达式 | [13579](?>A+B+) |
"1ABB", "3ABB"和 "5AB" 匹配 "1ABB 3ABBC 5AB 5AC" |
限定符
量词指定在输入字符串中必须存在多少个前一个元素(其可以是字符,组或字符类)的实例才能进行匹配。
| 修辞符 | 描述 | 模式 | 匹配 |
|---|---|---|---|
* |
匹配上一个元素零次或多次 | \d*\.\d |
".0", "19.9", "219.9" |
+ |
匹配上一个元素一次或多次。 | "be+" |
"bee"匹配"been", "be" 匹配 "bent" |
? |
匹配上一个元素零或一次。 | "rai?n" |
"ran", "rain" |
{ n } |
匹配上一个元素n次。 |
",\d{3}" |
",043"匹配"1,043.6", ",876", ",543",以及 ",210"匹配 "9,876,543,210" |
{ n ,} |
匹配上一个元素至少n次 |
"\d{2,}" |
"166", "29", "1930" |
{ n , m } |
匹配前一个元素至少n次,但不超过m次。 |
"\d{3,5}" |
"166", "17668", "19302"匹配 "193024" |
*? |
匹配上一个元素零次或更多次,但是次数尽可能少。 | \d*?\.\d |
".0", "19.9", "219.9" |
+? |
匹配上一个元素一次或多次,但是次数尽可能少。 | "be+?" |
"be" 匹配 "been", "be"匹配 "bent" |
?? |
匹配上一个元素零或一次,但是次数尽可能少。 | "rai??n" |
"ran", "rain" |
{ n }? |
匹配前一个元素n次。 |
",\d{3}?" |
",043"匹配 "1,043.6", ",876", ",543" 和 ",210"匹配 "9,876,543,210" |
{ n ,}? |
匹配前一个元素至少n次,但是次数尽可能少。 |
"\d{2,}?" |
"166", "29", "1930" |
{ n , m }? |
匹配上一个元素在n和m之间的次数,但是次数尽可能少。 |
"\d{3,5}?" |
"166", "17668","193", "024" 匹配 "193024" |
反向引用结构
反向引用构造允许上一个匹配的子表达式随后在相同的正则表达式中被识别。
下表列出了这些构造:
| 反向引用结构 | 描述 | 模式 | 匹配 |
|---|---|---|---|
\ number |
反向引用,匹配子表达式的值的数量 | (\w)\1 |
"ee"匹配"seek" |
\k< name > |
命名反向引用,匹配命名表达式的值 | (?< char>\w)\k< char> |
"ee"匹配"seek" |
交替构造
交替构造修改正则表达式以启用/或匹配。 下表列出了交替结构:
|- 匹配由垂直竖线(|)字符分隔的任何一个元素。模式:th(e|is|at),如:"the","this"匹配"this is the day. "(?( expression )yes | no )- 如果表达式匹配yes; 否则匹配可选的no部分。模式:(?(A)A\d{2}\b|\b\d{3}\b),如:"A10","910"匹配"A10 C103 910"(?( name )yes | no )- 如果命名的捕获名称具有匹配yes; 否则匹配可选的no部分。如:(?< quoted>")?(?(quoted).+?"|\S+\s),如:Dogs.jpg,"Yiska playing.jpg"匹配"Dogs.jpg "Yiska playing.jpg""
替换
替换用于替代模式,下表列出了替代模式的用法:
| 字符 | 描述 | 模式 | 替换模式 |
|---|---|---|---|
$number |
替换与分组数匹配的子串 | \b(\w+)(\s)(\w+)\b |
$3$2$1 |
${name} |
替换由命名组名匹配的子字符串。 | \b(?< word1>\w+)(\s)(?< word2>\w+)\b |
${word2} ${word1} |
$$ |
替换一个字面值“$” |
\b(\d+)\s?USD |
$$$1 |
$& |
替代整个匹配的副本 | (\$*(\d*(\.+\d+)?){1}) |
**$& |
| $` | 在匹配上一个替换输入字符串的所有文本 | B+ |
$` |
$+ |
替代被捕获的最后一个分组 | B+(C+) |
$+ |
$_ |
替换整个输入字符串 | B+ |
$_ |
其他结构
以下是各种杂结构:
| 构造 | 定义 | 示例 |
|---|---|---|
| (?imnsx-imnsx) | 设置或禁用的选项,如在图案的中间的情况下不敏感 | A(?i)bw+ 匹配"ABA", "Able" in "ABA Able Act" |
| (?#comment) | 内线注释。在第一个右括号注释结束 | A(?#匹配以A开始的词)w+ |
| # [to end of line] | X-模式注释。注释开始于一个转义#并继续到行的结尾 | (?x)Aw+#匹配以A开始的单词 |
Regex类
正则表达式 -Regex类用于表示正则表达式。 它有以下常用的方法:
| 序号 | 方法 | 描述 |
|---|---|---|
| 1 | public bool IsMatch(string input) | 指示在正则表达式构造函数中指定的正则表达式是否在指定的输入字符串中找到匹配项。 |
| 2 | public bool IsMatch(string input, int startat) | 指示在正则表达式构造函数中指定的正则表达式是否在指定的输入字符串(input)中找到匹配,从字符串中指定的起始(startat)位置开始。 |
| 3 | public static bool IsMatch(string input, string pattern) | 在指定的正则表达式是否在指定的输入字符串中找到匹配项。 |
| 4 | public MatchCollection Matches(string input) | 搜索所有出现正则表达式的指定输入字符串。 |
| 5 | public string Replace(string input, string replacement) | 在指定的输入字符串中,将与正则表达式模式匹配的所有字符串替换为指定的替换字符串(replacementreplacement)。 |
| 6 | public string[] Split(string input) | 将输入字符串拆分为由正则表达式构造函数中指定的正则表达式模式定义的位置的子字符串数组。 |
有关方法和属性的完整列表,请阅读Microsoft C# 文档。
实例1
以下示例匹配以“S”开头的单词:
using System;
using System.Text.RegularExpressions;
namespace RegExApplication
{
class Program
{
private static void showMatch(string text, string expr)
{
Console.WriteLine("The Expression: " + expr);
MatchCollection mc = Regex.Matches(text, expr);
foreach (Match m in mc)
{
Console.WriteLine(m);
}
}
static void Main(string[] args)
{
string str = "A Thousand Splendid Suns";
Console.WriteLine("Matching words that start with 'S': ");
showMatch(str, @"\bS\S*");
Console.ReadKey();
}
}
}
当编译和执行上述代码时,会产生以下结果:
Matching words that start with 'S':
The Expression: \bS\S*
Splendid
Suns
示例2
以下示例匹配以'm'开头并以'e'结尾的单词:
using System;
using System.Text.RegularExpressions;
namespace RegExApplication
{
class Program
{
private static void showMatch(string text, string expr)
{
Console.WriteLine("The Expression: " + expr);
MatchCollection mc = Regex.Matches(text, expr);
foreach (Match m in mc)
{
Console.WriteLine(m);
}
}
static void Main(string[] args)
{
string str = "make maze and manage to measure it";
Console.WriteLine("Matching words start with 'm' and ends with 'e':");
showMatch(str, @"\bm\S*e\b");
Console.ReadKey();
}
}
}
当编译和执行上述代码时,会产生以下结果:
Matching words start with 'm' and ends with 'e':
The Expression: \bm\S*e\b
make
maze
manage
measure
实例3
此示例替换了额外多余的空格:
using System;
using System.Text.RegularExpressions;
namespace RegExApplication
{
class Program
{
static void Main(string[] args)
{
string input = "Hello World ";
string pattern = "\\s+";
string replacement = " ";
Regex rgx = new Regex(pattern);
string result = rgx.Replace(input, replacement);
Console.WriteLine("Original String: {0}", input);
Console.WriteLine("Replacement String: {0}", result);
Console.ReadKey();
}
}
}
当编译和执行上述代码时,会产生以下结果:
Original String: Hello World
Replacement String: Hello World
异常处理
异常(例外)是在执行程序期间出现的问题。 C# 异常是对程序运行时出现的异常情况的响应,例如:除以零的算术运算。
异常提供了将控制从程序的一个部分转移到另一个程序的方法。 C# 异常处理建立在四个关键字上:try,catch,finally和throw。
- try:
try块标识一个特定异常被激活的代码块。try块之后有一个或多个catch块。 - catch:程序在处理问题的程序中的某个位置使用异常处理程序捕获异常。
catch关键字表示捕获异常。 - finally:
finally块用于执行给定的一组语句,无论抛出异常还是抛出异常。例如,如果打开文件,则不管是否引发异常,都必须关闭该文件。 - throw:当出现问题时,程序会抛出异常。这是使用
throw关键字完成的。
语法
假设一个块引发异常,一个方法使用try和catch关键字的组合来捕获异常。try/catch块放在可能会产生异常的代码周围。try/catch块中的代码被称为受保护代码,并且使用try/catch的语法如下所示:
try
{
// statements causing exception
}catch( ExceptionName e1 ){
// error handling code
}catch( ExceptionName e2 ){
// error handling code
}catch( ExceptionName eN ){
// error handling code
}finally{
// statements to be executed
}
可以列出多个catch语句来捕获不同类型的异常,以防try块在不同情况(case)下引发多个异常。
C# 异常类
C# 异常由类表示。 C# 中的异常类主要直接或间接地从System.Exception类派生。 从System.Exception类派生的一些异常类是System.ApplicationException和System.SystemException类。
System.ApplicationException类支持应用程序生成的异常。因此,程序员定义的异常应该从这个类派生出来。
System.SystemException类是所有预定义系统异常的基类。
下表列出了一些从System.SystemException类派生的一些预定义的异常类:
| 异常类 | 描述 |
|---|---|
| System.IO.IOException | 处理I/O错误 |
| System.IndexOutOfRangeException | 处理当方法引用数组索引超出范围时生成的错误。 |
| System.ArrayTypeMismatchException | 处理类型与数组类型不匹配时生成的错误。 |
| System.NullReferenceException | 处理从引用空(null)对象而产生的错误。 |
| System.DivideByZeroException | 处理除以零产生的错误。 |
| System.InvalidCastException | 处理类型转换过程中产生的错误。 |
| System.OutOfMemoryException | 处理由于空闲内存不足而产生的错误。 |
| System.StackOverflowException | 处理由于空闲内存不足而产生的错误。 |
| System.StackOverflowException | 处理堆栈溢出产生的错误。 |
处理异常
C# 以try和catch块的形式提供了异常处理的结构化解决方案。使用这些块,核心程序语句与错误处理语句分离。
这些错误处理块使用try,catch和finally关键字实现。下面是一个除以零条件发生异常时的异常:
using System;
namespace ErrorHandlingApplication
{
class DivNumbers
{
int result;
DivNumbers()
{
result = 0;
}
public void division(int num1, int num2)
{
try
{
result = num1 / num2;
}
catch (DivideByZeroException e)
{
Console.WriteLine("Exception caught: {0}", e);
}
finally
{
Console.WriteLine("Result: {0}", result);
}
}
static void Main(string[] args)
{
DivNumbers d = new DivNumbers();
d.division(25, 0);
Console.ReadKey();
}
}
}
当上述代码被编译并执行时,它产生以下结果:
Exception caught: System.DivideByZeroException: Attempted to divide by zero.
at ...
Result: 0
用户定义的异常
您也可以定义自己的异常。用户定义的异常类派生自Exception类。以下示例演示如何创建一个自定义异常:
using System;
namespace UserDefinedException
{
class TestTemperature
{
static void Main(string[] args)
{
Temperature temp = new Temperature();
try
{
temp.showTemp();
}
catch(TempIsZeroException e)
{
Console.WriteLine("TempIsZeroException: {0}", e.Message);
}
Console.ReadKey();
}
}
}
public class TempIsZeroException: Exception
{
public TempIsZeroException(string message): base(message)
{
}
}
public class Temperature
{
int temperature = 0;
public void showTemp()
{
if(temperature == 0)
{
throw (new TempIsZeroException("Zero Temperature found"));
}
else
{
Console.WriteLine("Temperature: {0}", temperature);
}
}
}
当上述代码被编译并执行时,它产生以下结果:
TempIsZeroException: Zero Temperature found
抛出对象
如果直接或间接派生自System.Exception类,则可以抛出一个对象。可以在catch块中使用throw语句将当前对象抛出:
Catch(Exception e)
{
...
Throw e
}
属性
属性是一个声明性的标签,用于向运行时传递有关程序中各种元素(如类,方法,结构,枚举器,程序集等)的行为的信息。可以通过使用属性将声明性信息添加到程序。声明式标签由放置在其所用元素上方的方括号([])表示。
属性用于向程序添加元数据,如编译器指令和其他信息,如注释,描述,方法和类。.Net框架提供了两种类型的属性:预定义属性和自定义构建的属性。
指定属性
用于指定属性的语法如下:
[attribute(positional_parameters, name_parameter = value, ...)]
element
属性名称及其值在方括号内,在应用该属性的元素之前指定。位置参数指定必要信息,名称参数指定可选信息。
预定义属性
.Net框架提供了三个预定义的属性:
- AttributeUsage
- Conditional
- Obsolete
预定义属性:AttributeUsage
预定义属性AttributeUsage描述了如何使用自定义属性类。它指定可以应用该属性的项目的类型。
用于指定此属性的语法如下:
[AttributeUsage(
validon,
AllowMultiple=allowmultiple,
Inherited=inherited
)]
其中,
- 参数
validon指定可以放置属性的语言元素。它是枚举器AttributeTargets的值的组合。默认值为AttributeTargets.All。 - 参数
allowmultiple(可选)为此属性的AllowMultiple属性提供了一个布尔值。 如果些值为:true,则表示属性是多次使用。默认值为false,表示一次性使用。 - 参数
inherited(可选)为此属性的Inherited属性提供了一个布尔值。 如果此参数值为:true,则属性由派生类继承。 它默认值为false(不继承)。
例如,
[AttributeUsage(AttributeTargets.Class |
AttributeTargets.Constructor |
AttributeTargets.Field |
AttributeTargets.Method |
AttributeTargets.Property,
AllowMultiple = true)]
预定义属性:Conditional
此预定义属性标记一个条件方法,其执行取决于指定的预处理标识符。
它使方法调用条件编译,具体取决于指定的值,如:调试(Debug)或跟踪(Trace)。 例如,它在调试代码时显示变量的值。
用于指定此属性的语法如下:
[Conditional(
conditionalSymbol
)]
例如,
[Conditional("DEBUG")]
以下示例演示了该属性:
#define DEBUG
using System;
using System.Diagnostics;
public class Myclass
{
[Conditional("DEBUG")]
public static void Message(string msg)
{
Console.WriteLine(msg);
}
}
class Test
{
static void function1()
{
Myclass.Message("In Function 1.");
function2();
}
static void function2()
{
Myclass.Message("In Function 2.");
}
public static void Main()
{
Myclass.Message("In Main function.");
function1();
Console.ReadKey();
}
}
当上述代码被编译并执行时,它产生以下结果:
In Main function
In Function 1
In Function 2
预定义属性: Obsolete
此预定义属性标记不应该使用的程序实体。它能够通知编译器丢弃特定的目标元素。 例如,当一个类中正在使用一个新方法,并且如果仍然希望在类中保留旧方法时,可以通过显示新方法而不是旧方法来显示消息来将其标记为过时。
用于指定此属性的语法如下:
[Obsolete(
message
)]
[Obsolete(
message,
iserror
)]
其中,
- 参数 -
message是一个字符串,描述项目过时的原因以及使用的替代方法。 - 参数 -
iserror,是一个布尔值。 如果值为true,则编译器应将该项目的使用视为错误。默认值为false,编译器生成警告。
示例程序如下:
using System;
public class MyClass
{
[Obsolete("Don't use OldMethod, use NewMethod instead", true)]
static void OldMethod()
{
Console.WriteLine("It is the old method");
}
static void NewMethod()
{
Console.WriteLine("It is the new method");
}
public static void Main()
{
OldMethod();
}
}
当尝试编译程序时,编译器会提供一条错误消息:
Don't use OldMethod, use NewMethod instead
创建自定义属性
.Net框架允许创建可用于存储声明性信息的自定义属性,并可在运行时检索。该信息可以根据设计标准和应用需要与任何目标元素相关。
创建和使用自定义属性涉及四个步骤:
- 声明一个自定义属性
- 构造自定义属性
- 将自定义属性应用于目标程序元素
- 通过反射访问属性
最后一步是编写一个简单的程序来读取元数据以找到各种标记。元数据是用于描述其他数据的数据或信息。程序可在运行时访问属性的反射。这将在下一章讨论。
声明自定义属性
应该从System.Attribute类派生一个新的自定义属性。 例如,
//a custom attribute BugFix to be assigned to a class and its members
[AttributeUsage(AttributeTargets.Class |
AttributeTargets.Constructor |
AttributeTargets.Field |
AttributeTargets.Method |
AttributeTargets.Property,
AllowMultiple = true)]
public class DeBugInfo : System.Attribute
在上面的代码中,声明了一个名称为DeBugInfo的自定义属性。
构造自定义属性
下面来构建一个名称为DeBugInfo的自定义属性,它存储通过调试任何程序获得的信息。它存储以下信息:
- 错误代码编号
- 识别错误的开发人员的名称
- 代码上次审查日期
- 用于存储开发人员言论的字符串消息
DeBugInfo类有三个私有属性用于存储前三个信息和一个用于存储消息的公共属性。 因此,错误编号,开发人员名称和审查日期是DeBugInfo类的位置参数,并且消息是可选的或命名的参数。
每个属性必须至少有一个构造函数。位置参数应通过构造函数传递。以下代码显示DeBugInfo类:
//a custom attribute BugFix to be assigned to a class and its members
[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, "Maxsu", "12/8/2018", Message = "Return type mismatch")]
[DeBugInfo(49, "Sukyda", "10/10/2018", Message = "Unused variable")]
class Rectangle
{
//member variables
protected double length;
protected double width;
public Rectangle(double l, double w)
{
length = l;
width = w;
}
[DeBugInfo(55, "Maxsu", "19/10/2018", Message = "Return type mismatch")]
public double GetArea()
{
return length * width;
}
[DeBugInfo(56, "Maxsu", "19/10/2018")]
public void Display()
{
Console.WriteLine("Length: {0}", length);
Console.WriteLine("Width: {0}", width);
Console.WriteLine("Area: {0}", GetArea());
}
}
在下一章中,使用Reflection类对象检索属性信息。


