一、类
语法
[public] class 类名 { 字段; 属性; 方法; }
类的定义是以关键字 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) { // method body } <access specifier> <return type> method2(parameter_list) { // method body } ... <access specifier> <return type> methodN(parameter_list) { // method body } }
请注意:
- 访问标识符 <access specifier> 指定了对类及其成员的访问规则。如果没有指定,则使用默认的访问标识符。类的默认访问标识符是 internal,成员的默认访问标识符是 private。
- 数据类型 <data type> 指定了变量的类型,返回类型 <return type> 指定了返回的方法返回的数据类型。
- 如果要访问类的成员,你要使用点(.)运算符。
- 点运算符链接了对象的名称和成员的名称。
VS右键项目,点 添加 ,选 类
编写类
using System; using System.Collections.Generic; using System.Linq; using System.Text; using System.Threading.Tasks; namespace HelloWorld { public class Person { public string _name; public int _age; public char _gender; public void action() { Console.WriteLine("我的名字是{0}, 性别{1}, {2}岁了。", this._name, this._gender, this._age); } } }
创建类的对象,该过程称之为类的实例化,使用关键字 new
using System; using System.Collections.Generic; using System.Linq; using System.Text; using System.Threading.Tasks; namespace HelloWorld { class Program { static void Main(string[] args) { Person person = new Person(); person._name = "wang"; person._gender = ‘男‘ ; person._age = 18; person.action(); Console.ReadKey(); } } }
属性
属性的作用是保护字段,对字段的赋值和取值进行限定。
属性的本质是两个方法:get和set,通过反编译工具Reflector.exe可以看到该结论。
using System; using System.Collections.Generic; using System.Linq; using System.Text; using System.Threading.Tasks; namespace HelloWorld { public class Person { public string _name; public int _age; public int Age { get { return _age; } set { if (value < 0 || value > 100) { _age = 0; } else { _age = value; } } } public char _gender; public void Action() { Console.WriteLine("我的名字是{0}, 性别{1}, {2}岁了。", this._name, this._gender, this.Age); } } }
使用属性
using System; using System.Collections.Generic; using System.Linq; using System.Text; using System.Threading.Tasks; namespace HelloWorld { class Program { static void Main(string[] args) { Person person = new Person(); person._name = "wang"; person._gender = ‘男‘ ; person.Age = 180; person.Action(); Console.ReadKey(); } } }
结果
静态和非静态
在非静态类中,既可以有实例成员,也可以有静态成员。
在调用实例成员的时候,需要使用 对象名.实例成员 ,在调用静态成员的时候,需要使用 类名.静态成员名 。
静态成员必须使用类名去调用,而实例成员使用对象名调用
静态函数中,只能访问静态成员,不允许访问实例成员,实例函数中,既可以使用静态成员,也可以使用实例成员。
静态类,只有静态成员,不允许出现实例成员,不能用new实例化。
工具类,一般使用静态类。
静态类在整个项目*享资源,整个程序运行结束才释放所占用资源。
构造函数
帮助我们初始化对象,是一个特殊的方法:
- 没有返回值,不写void关键字
- 名称和类名一致
- 必须有public
using System; namespace LineApplication { class Line { private double length; // 线条的长度 public Line() { Console.WriteLine("对象已创建"); } public void setLength( double len ) { length = len; } public double getLength() { return length; } static void Main(string[] args) { Line line = new Line(); // 设置线条长度 line.setLength(6.0); Console.WriteLine("线条的长度: {0}", line.getLength()); Console.ReadKey(); } } }
当上面的代码被编译和执行时,它会产生下列结果:
对象已创建
线条的长度: 6
默认的构造函数没有任何参数。但是如果你需要一个带有参数的构造函数可以有参数,这种构造函数叫做参数化构造函数。这种技术可以帮助你在创建对象的同时给对象赋初始值,具体请看下面实例:
using System; namespace LineApplication { class Line { private double length; // 线条的长度 public Line(double len) // 参数化构造函数 { Console.WriteLine("对象已创建,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(10.0); Console.WriteLine("线条的长度: {0}", line.getLength()); // 设置线条长度 line.setLength(6.0); Console.WriteLine("线条的长度: {0}", line.getLength()); Console.ReadKey(); } } }
析构函数
类的 析构函数 是类的一个特殊的成员函数,当类的对象超出范围时执行。
析构函数的名称是在类的名称前加上一个波浪形(~)作为前缀,它不返回值,也不带任何参数。
析构函数用于在结束程序(比如关闭文件、释放内存等)之前释放资源。析构函数不能继承或重载。
下面的实例说明了析构函数的概念:
using System; namespace LineApplication { class Line { private double length; // 线条的长度 public Line() // 构造函数 { Console.WriteLine("对象已创建"); } ~Line() //析构函数 { Console.WriteLine("对象已删除"); } public void setLength( double len ) { length = len; } public double getLength() { return length; } static void Main(string[] args) { Line line = new Line(); // 设置线条长度 line.setLength(6.0); Console.WriteLine("线条的长度: {0}", line.getLength()); } } }
命名空间
可以认为类属于命名空间,如果在当前项目中没有这个类的命名空间,需要手动导入这个类所在的命名空间。
在一个项目中引用另一个项目的类:
- 添加引用
- 引用命名空间
二、继承
父类
using System; using System.Collections.Generic; using System.Linq; using System.Text; using System.Threading.Tasks; namespace HelloWorld { public class Person { public string _name; private int _age; public int Age { get { return _age; } set { if (value < 0 || value > 100) { _age = 0; } else { _age = value; } } } public char _gender; public Person() { } public Person(string name, int age, char gender) { this._name = name; this.Age = age; this._gender = gender; } public void Action() { Console.WriteLine("我的名字是{0}, 性别{1}, {2}岁了。", this._name, this._gender, this.Age); } } }
子类继承了父类的属性和方法,但没继承私有属性
子类并没有继承父类的构造函数,子类不能调用父类的构造函数进行实例化。
但是子类会默认调用父类无参构造函数,在子类中创建父类对象,让子类可以使用父类中的成员。所以,如果在父类中重写了一个有参数的构造方法,无参构造函数就不存在了,子类就调用不到了,子类会报错。解决办法:a.父类中重写一个无参数的构造函数;b.在子类中显示的调用父类的构造函数,使用关键字base()
using System; using System.Collections.Generic; using System.Linq; using System.Text; using System.Threading.Tasks; namespace HelloWorld { class Student : Person { private int _id; public int ID { get { return _id; } set { _id = value; } } public Student(string name, int age, char gender, int id) : base(name, age, gender){ this.ID = id; } } }
继承的特性:
- 单根性:一个子类只能有一个父类。
- 继承的传递性
使用new关键字隐藏父类成员
也就是覆盖父类成员
using System; using System.Collections.Generic; using System.Linq; using System.Text; using System.Threading.Tasks; namespace HelloWorld { class Student : Person { private int _id; public int ID { get { return _id; } set { _id = value; } } public Student(string name, int age, char gender, int id) : base(name, age, gender){ this.ID = id; } public new void Action() { Console.WriteLine("Student Action!"); } } }
object是所有类的基类
里氏转换
- 子类可以赋值给父类
- 如果父类中装的是子类对象,那么可以将这个父类强转为子类
using System; using System.Collections.Generic; using System.Linq; using System.Text; using System.Threading.Tasks; namespace HelloWorld { class Program { static void Main(string[] args) { Person p = new Student("w", 20, ‘M‘, 5); Student student = (Student)p; student.Action(); Console.ReadKey(); } } }
is:表示类型转换,如果能够转换成功,则返回一个true,否则返回一个false
using System; using System.Collections.Generic; using System.Linq; using System.Text; using System.Threading.Tasks; namespace HelloWorld { /// <summary> /// /// </summary> class Program { static void Main(string[] args) { Person p = new Student("w", 20, ‘M‘, 5); if (p is Student) { Student student = (Student)p; Console.WriteLine("转换成功"); }else { Console.WriteLine("无法转换"); } Console.ReadKey(); } } }
as:表示类型转换,如果能够转换则返回对应的对象,否则返回一个null
using System; using System.Collections.Generic; using System.Linq; using System.Text; using System.Threading.Tasks; namespace HelloWorld { /// <summary> /// /// </summary> class Program { static void Main(string[] args) { Person p = new Student("w", 20, ‘M‘, 5); Student s = p as Student; s.Action(); Console.ReadKey(); } } }
三、集合
集合(Collection)类是专门用于数据存储和检索的类。这些类提供了对栈(stack)、队列(queue)、列表(list)和哈希表(hash table)的支持。大多数集合类实现了相同的接口。
集合(Collection)类服务于不同的目的,如为元素动态分配内存,基于索引访问列表项等等。这些类创建 Object 类的对象的集合。在 C# 中,Object 类是所有数据类型的基类。
各种集合类和它们的用法
下面是各种常用的 System.Collection 命名空间的类。点击下面的链接查看细节。
类 | 描述和用法 |
---|---|
动态数组(ArrayList) | 它代表了可被单独索引的对象的有序集合。
它基本上可以替代一个数组。但是,与数组不同的是,您可以使用索引在指定的位置添加和移除项目,动态数组会自动重新调整它的大小。它也允许在列表中进行动态内存分配、增加、搜索、排序各项。 |
哈希表(Hashtable) | 它使用键来访问集合中的元素。
当您使用键访问元素时,则使用哈希表,而且您可以识别一个有用的键值。哈希表中的每一项都有一个键/值对。键用于访问集合中的项目。 |
排序列表(SortedList) | 它可以使用键和索引来访问列表中的项。
排序列表是数组和哈希表的组合。它包含一个可使用键或索引访问各项的列表。如果您使用索引访问各项,则它是一个动态数组(ArrayList),如果您使用键访问各项,则它是一个哈希表(Hashtable)。集合中的各项总是按键值排序。 |
堆栈(Stack) | 它代表了一个后进先出的对象集合。
当您需要对各项进行后进先出的访问时,则使用堆栈。当您在列表中添加一项,称为推入元素,当您从列表中移除一项时,称为弹出元素。 |
队列(Queue) | 它代表了一个先进先出的对象集合。
当您需要对各项进行先进先出的访问时,则使用队列。当您在列表中添加一项,称为入队,当您从列表中移除一项时,称为出队。 |
点阵列(BitArray) | 它代表了一个使用值 1 和 0 来表示的二进制数组。
当您需要存储位,但是事先不知道位数时,则使用点阵列。您可以使用整型索引从点阵列集合中访问各项,索引从零开始。 |
ArrayList
动态数组(ArrayList)代表了可被单独索引的对象的有序集合。它基本上可以替代一个数组。但是,与数组不同的是,您可以使用索引在指定的位置添加和移除项目,动态数组会自动重新调整它的大小。它也允许在列表中进行动态内存分配、增加、搜索、排序各项。
下表列出了 ArrayList 类的一些常用的 属性:
属性 | 描述 |
---|---|
Capacity | 获取或设置 ArrayList 可以包含的元素个数。 |
Count | 获取 ArrayList 中实际包含的元素个数。 |
IsFixedSize | 获取一个值,表示 ArrayList 是否具有固定大小。 |
IsReadOnly | 获取一个值,表示 ArrayList 是否只读。 |
IsSynchronized | 获取一个值,表示访问 ArrayList 是否同步(线程安全)。 |
Item[Int32] | 获取或设置指定索引处的元素。 |
SyncRoot | 获取一个对象用于同步访问 ArrayList。 |
下表列出了 ArrayList 类的一些常用的 方法:
序号 | 方法名 & 描述 |
---|---|
1 |
public virtual int Add( object value ); 在 ArrayList 的末尾添加一个对象。 |
2 |
public virtual void AddRange( ICollection c ); 在 ArrayList 的末尾添加 ICollection 的元素。 |
3 |
public virtual void Clear(); 从 ArrayList 中移除所有的元素。 |
4 |
public virtual bool Contains( object item ); 判断某个元素是否在 ArrayList 中。 |
5 |
public virtual ArrayList GetRange( int index, int count ); 返回一个 ArrayList,表示源 ArrayList 中元素的子集。 |
6 |
public virtual int IndexOf(object); 返回某个值在 ArrayList 中第一次出现的索引,索引从零开始。 |
7 |
public virtual void Insert( int index, object value ); 在 ArrayList 的指定索引处,插入一个元素。 |
8 |
public virtual void InsertRange( int index, ICollection c ); 在 ArrayList 的指定索引处,插入某个集合的元素。 |
9 |
public virtual void Remove( object obj ); 从 ArrayList 中移除第一次出现的指定对象。 |
10 |
public virtual void RemoveAt( int index ); 移除 ArrayList 的指定索引处的元素。 |
11 |
public virtual void RemoveRange( int index, int count ); 从 ArrayList 中移除某个范围的元素。 |
12 |
public virtual void Reverse(); 逆转 ArrayList 中元素的顺序。 |
13 |
public virtual void SetRange( int index, ICollection c ); 复制某个集合的元素到 ArrayList 中某个范围的元素上。 |
14 |
public virtual void Sort(); 对 ArrayList 中的元素进行排序。 |
15 |
public virtual void TrimToSize(); 设置容量为 ArrayList 中元素的实际个数。 |
示例
using System; using System.Collections; namespace CollectionApplication { class Program { static void Main(string[] args) { ArrayList al = new ArrayList(); Console.WriteLine("Adding some numbers:"); al.Add(45); al.Add(78); al.Add(33); al.Add(56); al.Add(12); al.Add(23); al.Add(9); Console.WriteLine("Capacity: {0} ", al.Capacity); Console.WriteLine("Count: {0}", al.Count); Console.Write("Content: "); foreach (int i in al) { Console.Write(i + " "); } Console.WriteLine(); Console.Write("Sorted Content: "); al.Sort(); foreach (int i in al) { Console.Write(i + " "); } Console.WriteLine(); Console.ReadKey(); } } }
HashTable
Hashtable 类代表了一系列基于键的哈希代码组织起来的键/值对。它使用键来访问集合中的元素。
当您使用键访问元素时,则使用哈希表,而且您可以识别一个有用的键值。哈希表中的每一项都有一个键/值对。键用于访问集合中的项目。
Hashtable 类的方法和属性
下表列出了 Hashtable 类的一些常用的 属性:
属性 | 描述 |
---|---|
Count | 获取 Hashtable 中包含的键值对个数。 |
IsFixedSize | 获取一个值,表示 Hashtable 是否具有固定大小。 |
IsReadOnly | 获取一个值,表示 Hashtable 是否只读。 |
Item | 获取或设置与指定的键相关的值。 |
Keys | 获取一个 ICollection,包含 Hashtable 中的键。 |
Values | 获取一个 ICollection,包含 Hashtable 中的值。 |
下表列出了 Hashtable 类的一些常用的 方法:
序号 | 方法名 & 描述 |
---|---|
1 |
public virtual void Add( object key, object value ); 向 Hashtable 添加一个带有指定的键和值的元素。 |
2 |
public virtual void Clear(); 从 Hashtable 中移除所有的元素。 |
3 |
public virtual bool ContainsKey( object key ); 判断 Hashtable 是否包含指定的键。 |
4 |
public virtual bool ContainsValue( object value ); 判断 Hashtable 是否包含指定的值。 |
5 |
public virtual void Remove( object key ); 从 Hashtable 中移除带有指定的键的元素。 |
示例
using System; using System.Collections; namespace CollectionsApplication { class Program { static void Main(string[] args) { Hashtable ht = new Hashtable(); ht.Add("001", "Zara Ali"); ht.Add("002", "Abida Rehman"); ht.Add("003", "Joe Holzner"); ht.Add("004", "Mausam Benazir Nur"); ht.Add("005", "M. Amlan"); ht.Add("006", "M. Arif"); ht.Add("007", "Ritesh Saikia"); if (ht.ContainsValue("Nuha Ali")) { Console.WriteLine("This student name is already in the list"); } else { ht.Add("008", "Nuha Ali"); } // 获取键的集合 ICollection key = ht.Keys; foreach (string k in key) { Console.WriteLine(k + ": " + ht[k]); } Console.ReadKey(); } } }
四、文件读写
一个 文件 是一个存储在磁盘中带有指定名称和目录路径的数据集合。当打开文件进行读写时,它变成一个 流。
从根本上说,流是通过通信路径传递的字节序列。有两个主要的流:输入流 和 输出流。输入流用于从文件读取数据(读操作),输出流用于向文件写入数据(写操作)。
C# I/O 类
System.IO 命名空间有各种不同的类,用于执行各种文件操作,如创建和删除文件、读取或写入文件,关闭文件等。
下表列出了一些 System.IO 命名空间中常用的非抽象类:
I/O 类 | 描述 |
---|---|
BinaryReader | 从二进制流读取原始数据。 |
BinaryWriter | 以二进制格式写入原始数据。 |
BufferedStream | 字节流的临时存储。 |
Directory | 有助于操作目录结构。 |
DirectoryInfo | 用于对目录执行操作。 |
DriveInfo | 提供驱动器的信息。 |
File | 有助于处理文件。 |
FileInfo | 用于对文件执行操作。 |
FileStream | 用于文件中任何位置的读写。 |
MemoryStream | 用于随机访问存储在内存中的数据流。 |
Path | 对路径信息执行操作。 |
StreamReader | 用于从字节流中读取字符。 |
StreamWriter | 用于向一个流中写入字符。 |
StringReader | 用于读取字符串缓冲区。 |
StringWriter | 用于写入字符串缓冲区。 |
Path类
在 C# 语言中 Path 类主要用于文件路径的一些操作,它也是一个静态类。
Path 类中常用的属性和方法如下表所示。
属性或方法 | 作用 |
---|---|
string ChangeExtension(string path, string extension) | 更改路径字符串的扩展名 |
string Combine(params string[] paths) | 将字符串数组组合成一个路径 |
string Combine(string path1, string path2) | 将两个字符串组合成一个路径 |
string GetDirectoryName(string path) | 返回指定路径字符串的目录信息 |
string GetExtension(string path) | 返回指定路径字符串的扩展名 |
string GetFileName(string path) | 返回指定路径字符串的文件名和扩展名 |
string GetFileNameWithoutExtension(string path) | 返回不具有扩展名的指定路径字符串的文件名 |
string GetFullPath(string path) | 返回指定路径字符串的绝对路径 |
char[] GetInvalidFileNameChars() | 获取包含不允许在文件名中使用的字符的数组 |
char[] GetInvalidPathChars() | 获取包含不允许在路径名中使用的字符的数组 |
string GetPathRoot(string path) | 获取指定路径的根目录信息 |
string GetRandomFileName() | 返回随机文件夹名或文件名 |
string GetTempPath() | 返回当前用户的临时文件夹的路径 |
bool HasExtension(string path) | 返回路径是否包含文件的扩展名 |
bool IsPathRooted(string path) | 返回路径字符串是否包含根 |
下面通过实例来演示 Path 类的应用。
【实例】从控制台输入一个路径,输出该路径的不含扩展名的路径、扩展名、文件全 名、文件路径、更改文件扩展名。
根据题目要求,代码如下。
class Program { static void Main(string[] args) { Console.WriteLine("请输入一个文件路径:"); string path = Console.ReadLine(); Console.WriteLine("不包含扩展名的文件名:" + Path.GetFileNameWithoutExtension(path)); Console.WriteLine("文件扩展名:" + Path.GetExtension(path)); Console.WriteLine("文件全名:" + Path.GetFileName(path)); Console.WriteLine("文件路径:" + Path.GetDirectoryName(path)); //更改文件扩展名 string newPath = Path.ChangeExtension(path, "doc"); Console.WriteLine("更改后的文件全名:" + Path.GetFileName(newPath)); } }
File类:文件操作
C# 语言中 File 类同样可以完成与 FileInfo 类相似的功能,但 File 类中也提供了一些不同的方法。
File 类中获取或设置文件信息的常用方法如下表所示。
属性或方法 | 作用 |
---|---|
DateTime GetCreationTime(string path) | 返回指定文件或目录的创建日期和时间 |
DateTime GetLastAccessTime(string path) | 返回上次访问指定文件或目录的日期和时间 |
DateTime GetLastWriteTime(string path) | 返回上次写入指定文件或目录的日期和时间 |
void SetCreationTime(string path, DateTime creationTime) | 设置创建该文件的日期和时间 |
void SetLastAccessTime(string path, DateTime lastAccessTime) | 设置上次访问指定文件的日期和时间 |
void SetLastWriteTime(string path, DateTime lastWriteTime) | 设置上次写入指定文件的日期和时间 |
File 类是静态类,所提供的类成员也是静态的,调用其类成员直接使用 File 类的名称调用即可。
【实例】
根据题目要求,代码如下。
class Program { static void Main(string[] args) { //在D盘下创建code文件夹 Directory.CreateDirectory("D:\\code"); Directory.CreateDirectory("D:\\code-1"); string path = "D:\\code\\test1.txt"; //创建文件 FileStream fs = File.Create(path); //获取文件信息 Console.WriteLine("文件创建时间:" + File.GetCreationTime(path)); Console.WriteLine("文件最后被写入时间:" + File.GetLastWriteTime(path)); //关闭文件流 fs.Close(); //设置目标路径 string newPath = "D:\\code-1\\test1.txt"; //判断目标文件是否存在 bool flag = File.Exists(newPath); if (flag) { //删除文件 File.Delete(newPath); } File.Move(path, newPath); } }
FileStream 类
System.IO 命名空间中的 FileStream 类有助于文件的读写与关闭。该类派生自抽象类 Stream。
您需要创建一个 FileStream 对象来创建一个新的文件,或打开一个已有的文件。创建 FileStream 对象的语法如下:
FileStream <object_name> = new FileStream( <file_name>, <FileMode Enumerator>, <FileAccess Enumerator>, <FileShare Enumerator>);
例如,创建一个 FileStream 对象 F 来读取名为 sample.txt 的文件:
FileStream F = new FileStream("sample.txt", FileMode.Open, FileAccess.Read, FileShare.Read);
参数 | 描述 |
---|---|
FileMode |
FileMode 枚举定义了各种打开文件的方法。FileMode 枚举的成员有:
|
FileAccess |
FileAccess 枚举的成员有:Read、ReadWrite 和 Write。 |
FileShare |
FileShare 枚举的成员有:
|
实例
下面的程序演示了 FileStream 类的用法:
实例
using System;
using System.IO;
namespace FileIOApplication
{
class Program
{
static void Main(string[] args)
{
FileStream F = new FileStream("test.dat", FileMode.OpenOrCreate, FileAccess.ReadWrite);
for (int i = 1; i <= 20; i++)
{
F.WriteByte((byte)i);
}
F.Position = 0;
for (int i = 0; i <= 20; i++)
{
Console.Write(F.ReadByte() + " ");
}
F.Close();
Console.ReadKey();
}
}
}
StreamReader 类
StreamReader 和 StreamWriter 类用于文本文件的数据读写。这些类从抽象基类 Stream 继承,Stream 支持文件流的字节读写。
StreamReader 类继承自抽象基类 TextReader,表示阅读器读取一系列字符。
下表列出了 StreamReader 类中一些常用的方法:
序号 | 方法 & 描述 |
---|---|
1 |
public override void Close() 关闭 StreamReader 对象和基础流,并释放任何与读者相关的系统资源。 |
2 |
public override int Peek() 返回下一个可用的字符,但不使用它。 |
3 |
public override int Read() 从输入流中读取下一个字符,并把字符位置往前移一个字符。 |
如需查看完整的方法列表,请访问微软的 C# 文档。
实例
下面的实例演示了读取名为 Jamaica.txt 的文件。文件如下:
Down the way where the nights are gay
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
using System; using System.IO; namespace FileApplication { class Program { static void Main(string[] args) { try { // 创建一个 StreamReader 的实例来读取文件 // using 语句也能关闭 StreamReader using (StreamReader sr = new StreamReader("c:/jamaica.txt")) { string line; // 从文件读取并显示行,直到文件的末尾 while ((line = sr.ReadLine()) != null) { Console.WriteLine(line); } } } catch (Exception e) { // 向用户显示出错消息 Console.WriteLine("The file could not be read:"); Console.WriteLine(e.Message); } Console.ReadKey(); } } }
当您编译和执行上面的程序时,它会显示文件的内容。
StreamWriter 类
StreamWriter 类继承自抽象类 TextWriter,表示编写器写入一系列字符。
下表列出了 StreamWriter 类中一些常用的方法:
序号 | 方法 & 描述 |
---|---|
1 |
public override void Close() 关闭当前的 StreamWriter 对象和基础流。 |
2 |
public override void Flush() 清理当前编写器的所有缓冲区,使得所有缓冲数据写入基础流。 |
3 |
public virtual void Write(bool value) 把一个布尔值的文本表示形式写入到文本字符串或流。(继承自 TextWriter。) |
4 |
public override void Write( char value ) 把一个字符写入到流。 |
5 |
public virtual void Write( decimal value ) 把一个十进制值的文本表示形式写入到文本字符串或流。 |
6 |
public virtual void Write( double value ) 把一个 8 字节浮点值的文本表示形式写入到文本字符串或流。 |
7 |
public virtual void Write( int value ) 把一个 4 字节有符号整数的文本表示形式写入到文本字符串或流。 |
8 |
public override void Write( string value ) 把一个字符串写入到流。 |
9 |
public virtual void WriteLine() 把行结束符写入到文本字符串或流。 |
如需查看完整的方法列表,请访问微软的 C# 文档。
实例
下面的实例演示了使用 StreamWriter 类向文件写入文本数据:
using System; using System.IO; namespace FileApplication { class Program { static void Main(string[] args) { string[] names = new string[] {"Zara Ali", "Nuha Ali"}; using (StreamWriter sw = new StreamWriter("names.txt")) { foreach (string s in names) { sw.WriteLine(s); } } // 从文件中读取并显示每行 string line = ""; using (StreamReader sr = new StreamReader("names.txt")) { while ((line = sr.ReadLine()) != null) { Console.WriteLine(line); } } Console.ReadKey(); } } }
当上面的代码被编译和执行时,它会产生下列结果:
Zara Ali Nuha Ali
using(){} 作为语句,用于定义一个范围,在此范围的末尾将释放对象。
using 语句允许程序员指定使用资源的对象应当何时释放资源。using 语句中使用的对象必须实现 IDisposable 接口。此接口提供了 Dispose 方法,该方法将释放此对象的资源。
当我们做一些比较占用资源的操作,而且该类或者它的父类继承了IDisposable接口,这样就可以使用using语句,在此范围的末尾自动将对象释放,常见的using使用在对数据库的操作的时候。
五、多态
多态是同一个行为具有多个不同表现形式或形态的能力。
多态性意味着有多重形式。在面向对象编程范式中,多态性往往表现为"一个接口,多个功能"。
多态性可以是静态的或动态的。在静态多态性中,函数的响应是在编译时发生的。在动态多态性中,函数的响应是在运行时发生的。
在 C# 中,每个类型都是多态的,因为包括用户定义类型在内的所有类型都继承自 Object。
静态多态性
在编译时,函数和对象的连接机制被称为早期绑定,也被称为静态绑定。C# 提供了两种技术来实现静态多态性。分别为:
- 函数重载
- 运算符重载
动态多态性
C# 允许您使用关键字 abstract 创建抽象类,用于提供接口的部分类的实现。
实现多态的三种手段:
- 虚方法
- 抽象类
- 接口
一个父类
using System; using System.Collections.Generic; using System.Linq; using System.Text; using System.Threading.Tasks; namespace HelloWorld { public class Person { private string _name; public string Name { get { return _name; } set { _name = value; } } public Person(string name) { this._name = name; } public void SayHello() { Console.WriteLine("我是地球人,我的的名字是{0}。", this.Name); } } }
两个子类
中国人
using System; using System.Collections.Generic; using System.Linq; using System.Text; using System.Threading.Tasks; namespace HelloWorld { class Chinese: Person { public Chinese(string name) : base(name) { this.Name = name; } public void SayHello() { Console.WriteLine("我是中国人,我的名字是{0}。", this.Name); } } }
美国人
using System; using System.Collections.Generic; using System.Linq; using System.Text; using System.Threading.Tasks; namespace HelloWorld { class American: Person { public American(string name) : base( name) { this.Name = name; } public void SayHello() { Console.WriteLine("我是美国人,我的名字是{0}。", this.Name); } } }
调用SayHello()方法
using System; using System.Collections.Generic; using System.Linq; using System.Text; using System.Threading.Tasks; namespace HelloWorld { class Program { static void Main(string[] args) { American a = new American("Tom"); Chinese c = new Chinese("王"); Person[] person = new Person[]{ a, c }; foreach(Person p in person) { p.SayHello(); } Console.ReadKey(); } } }
结果
实际使用的都是父类的方法
如果想使用子类的方法
using System; using System.Collections.Generic; using System.Linq; using System.Text; using System.Threading.Tasks; namespace HelloWorld { class Program { static void Main(string[] args) { American a = new American("Tom"); Chinese c = new Chinese("王"); Person[] person = new Person[]{ a, c }; foreach(Person p in person) { if (p is Chinese) { ((Chinese)p).SayHello(); } if(p is American) { ((American)p).SayHello(); } } Console.ReadKey(); } } }
结果
虚方法
当有一个定义在类中的函数需要在继承类中实现时,可以使用虚方法。
虚方法是使用关键字 virtual 声明的。
虚方法可以在不同的继承类中有不同的实现。
对虚方法的调用是在运行时发生的。
步骤:
- 将父类方法标记为虚方法,使用关键字virtual,该函数可以被子类重新写
- 子类重写虚方法,用override关键字
父类
using System; using System.Collections.Generic; using System.Linq; using System.Text; using System.Threading.Tasks; namespace HelloWorld { public class Person { private string _name; public string Name { get { return _name; } set { _name = value; } } public Person(string name) { this._name = name; } public virtual void SayHello() { Console.WriteLine("我是地球人,我的的名字是{0}。", this.Name); } } }
子类
using System; using System.Collections.Generic; using System.Linq; using System.Text; using System.Threading.Tasks; namespace HelloWorld { class Chinese: Person { public Chinese(string name) : base(name) { this.Name = name; } public override void SayHello() { Console.WriteLine("我是中国人,我的名字是{0}。", this.Name); } } }
using System; using System.Collections.Generic; using System.Linq; using System.Text; using System.Threading.Tasks; namespace HelloWorld { class American: Person { public American(string name) : base( name) { this.Name = name; } public override void SayHello() { Console.WriteLine("我是美国人,我的名字是{0}。", this.Name); } } }
实现多态
using System; using System.Collections.Generic; using System.Linq; using System.Text; using System.Threading.Tasks; namespace HelloWorld { class Program { static void Main(string[] args) { American a = new American("Tom"); Chinese c = new Chinese("王"); Person[] person = new Person[]{ a, c }; foreach(Person p in person) { p.SayHello(); } Console.ReadKey(); } } }
结果
抽象类
C# 允许您使用关键字 abstract 创建抽象类,用于提供接口的部分类的实现。当一个派生类继承自该抽象类时,实现即完成。抽象类包含抽象方法,抽象方法可被派生类实现。派生类具有更专业的功能。
请注意,下面是有关抽象类的一些规则:
- 您不能创建一个抽象类的实例。
- 您不能在一个抽象类外部声明一个抽象方法。
- 通过在类定义前面放置关键字 sealed,可以将类声明为密封类。当一个类被声明为 sealed 时,它不能被继承。抽象类不能被声明为 sealed。
下面的程序演示了一个抽象类:
实例
using System; namespace PolymorphismApplication { abstract class Shape { abstract public int area(); } class Rectangle: Shape { private int length; private int width; public Rectangle( int a=0, int b=0) { length = a; width = b; } public override int area () { Console.WriteLine("Rectangle 类的面积:"); return (width * length); } } class RectangleTester { static void Main(string[] args) { Rectangle r = new Rectangle(10, 7); double a = r.area(); Console.WriteLine("面积: {0}",a); Console.ReadKey(); } } }
当上面的代码被编译和执行时,它会产生下列结果:
Rectangle 类的面积:
面积: 70
动态多态性是通过 抽象类 和 虚方法 实现的。
接口
接口定义了所有类继承接口时应遵循的语法合同。接口定义了语法合同 "是什么" 部分,派生类定义了语法合同 "怎么做" 部分。
接口定义了属性、方法和事件,这些都是接口的成员。接口只包含了成员的声明。成员的定义是派生类的责任。接口提供了派生类应遵循的标准结构。
接口使得实现接口的类或结构在形式上保持一致。
抽象类在某种程度上与接口类似,但是,它们大多只是用在当只有少数方法由基类声明由派生类实现时。
定义接口: 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() , 方法名必须与接口定义的方法名一致。
接口继承: InterfaceInheritance.cs
以下实例定义了两个接口 IMyInterface 和 IParentInterface。
如果一个接口继承其他接口,那么实现类或结构就需要实现所有接口的成员。
以下实例 IMyInterface 继承了 IParentInterface 接口,因此接口实现类必须实现 MethodToImplement() 和 ParentInterfaceMethod() 方法:
实例
using System; interface IParentInterface { void ParentInterfaceMethod(); } interface IMyInterface : IParentInterface { void MethodToImplement(); } class InterfaceImplementer : IMyInterface { static void Main() { InterfaceImplementer iImp = new InterfaceImplementer(); iImp.MethodToImplement(); iImp.ParentInterfaceMethod(); } public void MethodToImplement() { Console.WriteLine("MethodToImplement() called."); } public void ParentInterfaceMethod() { Console.WriteLine("ParentInterfaceMethod() called."); } }
实例输出结果为:
MethodToImplement() called.
ParentInterfaceMethod() called.
六、泛型
泛型(Generic) 允许您延迟编写类或方法中的编程元素的数据类型的规范,直到实际在程序中使用它的时候。换句话说,泛型允许您编写一个可以与任何数据类型一起工作的类或方法。
您可以通过数据类型的替代参数编写类或方法的规范。当编译器遇到类的构造函数或方法的函数调用时,它会生成代码来处理指定的数据类型。下面这个简单的实例将有助于您理解这个概念:
实例
using System; using System.Collections.Generic; namespace GenericApplication { public class MyGenericArray<T> { private T[] array; public MyGenericArray(int size) { array = new T[size + 1]; } public T getItem(int index) { return array[index]; } public void setItem(int index, T value) { array[index] = value; } } class Tester { static void Main(string[] args) { // 声明一个整型数组 MyGenericArray<int> intArray = new MyGenericArray<int>(5); // 设置值 for (int c = 0; c < 5; c++) { intArray.setItem(c, c*5); } // 获取值 for (int c = 0; c < 5; c++) { Console.Write(intArray.getItem(c) + " "); } Console.WriteLine(); // 声明一个字符数组 MyGenericArray<char> charArray = new MyGenericArray<char>(5); // 设置值 for (int c = 0; c < 5; c++) { charArray.setItem(c, (char)(c+97)); } // 获取值 for (int c = 0; c < 5; c++) { Console.Write(charArray.getItem(c) + " "); } Console.WriteLine(); Console.ReadKey(); } } }
当上面的代码被编译和执行时,它会产生下列结果:
0 5 10 15 20
a b c d e
泛型(Generic)的特性
使用泛型是一种增强程序功能的技术,具体表现在以下几个方面:
- 它有助于您最大限度地重用代码、保护类型的安全以及提高性能。
- 您可以创建泛型集合类。.NET 框架类库在 System.Collections.Generic 命名空间中包含了一些新的泛型集合类。您可以使用这些泛型集合类来替代 System.Collections 中的集合类。
- 您可以创建自己的泛型接口、泛型类、泛型方法、泛型事件和泛型委托。
- 您可以对泛型类进行约束以访问特定数据类型的方法。
- 关于泛型数据类型中使用的类型的信息可在运行时通过使用反射获取。
泛型(Generic)方法
在上面的实例中,我们已经使用了泛型类,我们可以通过类型参数声明泛型方法。下面的程序说明了这个概念:
实例
using System; using System.Collections.Generic; namespace GenericMethodAppl { 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
泛型(Generic)委托
您可以通过类型参数定义泛型委托。例如:
delegate T NumberChanger<T>(T n);
下面的实例演示了委托的使用:
实例
using System; using System.Collections.Generic; delegate T NumberChanger<T>(T n); namespace GenericDelegateAppl { class TestDelegate { 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