Net Core教程

C#基础教程二

本文主要是介绍C#基础教程二,对大家解决编程问题具有一定的参考价值,需要的程序猿们随着小编来一起学习吧!

C#基础教程二

  • 类与对象
    • 概述
      • 基本概念
      • 类、方法和变量
      • 对象的创建及使用
    • 类的方法与属性
      • 方法定义
      • 方法调用
      • 方法中的参数传递
      • 方法重载
      • 类方法扩展实例
    • 构造函数和析构函数
      • 构造函数
      • 析构函数
    • 封装性
      • 概述
      • 属性
      • 索引器
    • 类的继承
      • 概述
      • base使用
    • 多态
      • 概述
      • 实现多态的方式
      • 虚方法与重写
      • 抽象类与抽象方法
    • 猜拳游戏
    • 系统登录用户类
  • 接口
    • 接口概念
    • 接口声明
    • 接口应用
  • 委托
    • 委托定义
    • 委托声明
    • 委托应用
    • 匿名方法
  • 事件
    • 事件定义
    • 事件使用
  • 目录与文件管理

类与对象

概述

面向对象程序设计(Object Oriented Programming,OOP)= 对象+类+继承+多态+消息。
程序由一系列对象组成。
类是现实世界抽象,包括数据和方法,对象是类的实例化。
对象间通过消息传递相互通信。

基本概念

  1. 对象
    对象是现实世界中事物存在的实体,分为静态部分(属性)和动态部分(可以变化的行为)。

  2. 类是具有相同属性和功能的对象的抽象集合,是C#的核心和基本构成模块。
    类是一种抽象的数据类型,是对一类对象的统一描述。

类是对象的定义,包含对象的信息,名称、方法、属性和事件。类并不是对象,因为它不存在于内存中。

类、方法和变量

  1. 类的定义

类定义可在多个源文件间拆分,partial。

[类修饰符] class 类名{
	类的主体
}

类修饰符:

  • public:共有,访问不受限制。
  • private:私有,私有成员只在声明的类或结构体中才可访问。
  • protected:保护,类内部及继承类中才能访问。
  • internal:内部,同一程序集内所有类可访问,一般限于本项目内。
  • protected internal:内部保护,限于本项目或子类访问。
  1. 类的成员变量
[访问修饰符] 数据类型 <成员变量名>

static修饰用于静态成员。
非静态类中可以出现[非]静态成员,静态类中只能出现静态成员。
非静态方法中可以访问[非]静态成员,静态方法中只能访问静态成员。
调用方法不同,实例方法需要对象调用,即对象名.方法名,静态方法使用类调用,即类名.方法名。
静态类不允许创建对象。
工具类,可以考虑使用静态类(Console类),资源共享,但占用内存大。

  1. 类的成员方法的定义
[访问修饰符] 返回值类型 <方法名> ([参数列表]){
	成员方法体
}
public class Car{
	public double Number;
	public string Make;
	public string Model;
	public string show(){
		return string.Format("排量:{0}T,厂家:{1},型号:{2}",Numbetr,Make,Model);
	}
}

对象的创建及使用

类是广义的数据类型,包含数据和数据的方法。
new建立类的新实例(对象)。

  1. 对象的创建和使用
    (1)先声明对象,再创建对象初始化。
Car myCar;
myCar = new Car();

(2)声明并创建对象。

Car myCar = new Car();
  1. 访问类成员
    (1)类内部访问。

this可省略。

this.类成员

this也可用于执行构造函数。

public class Test{
	public Test(){
		Console.WriteLine("无参构造函数");
	}
	//先执行Test(),后执行Test(string text)
	public Test(string text):this(){
		Console.WriteLine(text);
		Console.WriteLine("有参构造函数");
	}
}

(2)类外部调用。

对象名.成员
string str;
Car myCar = new Car();
myCar.Number = 2.0;
myCar.Make = "中国吉利";
myCar.Model = "GL";
str = myCar.show();

类的方法与属性

类的方法指对象执行的操作,类的属性指对象所拥有的特征。

方法定义

方法是将完成同一功能的内容放在一起,方便书写和调用。

访问修饰符 修饰符 返回值类型 方法名(参数列表){
	语句块;
} 
public double Add(double num1, double num2){
	return num1+num2;
}
  • 访问修饰符:public等,省略默认为private。
  • 修饰符:virtual(虚拟的)、abstract(抽象的)、override(重写的,用于继承)、static(静态的)、sealed(密封的)。
  • 返回值类型:任意数据类型,void表示无返回值。
  • 方法名:方法功能的描述。
  • 参数列表:0到多个参数。”数据类型 参数名“,多参数逗号隔开。
  • 语句块:方法体,执行的代码块。

方法调用

使用方法的过程。

方法名(参数列表);

方法中的参数传递

方法调用从外部传递的参数是实参,方法内部接收的参数是形参。
形参调用时才分配内存,调用结束释放内存,只在方法内部有效。

  1. 值类型参数

无任何修饰符,按值传递,形参和实参互不影响。

public void Swap(int x, int y) {

}
  1. 引用型参数

传递变量引用,实参和形参的引用指向内存中的同一位置。形参的更改会影响实参指向的变量。方法定义和调用都必须使用ref。

public void Swap(ref int x, ref int y) {
	int tmp;
	tmp = x;
	x = y;
	y = tmp;
}

int a = 6, b = 8;
Swap(ref a, ref b);
  1. 输出型参数

不创建新的存储位置,形参使用实参的存储位置,方法定义和调用都使用out。

public void Add(out int a) {
	a = 1;
}

int a;
Add(out a);
  1. 数组型参数

传递数组时,是引用传递,形参数组不能定义数组长度。
形参数组前不添加params修饰符,实参必须是数组名;形参数组前添加params修饰符,实参可以是数组名,也可以是数组元素值的列表(数据列表)。
params修饰符要点:

  • 可以修饰任何类型参数;
  • 只能修饰一维数组;
  • 不允许params数组使用ref或out;
  • 每个方法只能有一个params数组。
public int max1(int []a){
	int k = 0;
	for(int i=0;i<a.Length;i++){
		if(a[i]>a[k])
			k = i;
	}
	return a[k];
}

public int max2(params int []a){
	int k = 0;
	for(int i=0;i<a.Length;i++){
		if(a[i]>a[k])
			k = i;
	}
	return a[k];
}

int[] x = new int[]{1,2,3,4,5};
max1(x);
max2(x);
max2(1,2,3,4,5);

方法重载

定义多个方法名相同、参数列表(参数类型、参数个数)不同的方法。

int auto(int a, int b){
	return a+b;
}
string auto(string a, string b){
	return a+b;
}
int auto(int a, int b, int c){
	return a+b+c;
}

类方法扩展实例

Program.cs

using System;
using System.Collections.Generic;
using System.Linq;
using System.Text;
using System.Threading.Tasks;

namespace experiment2{
    static class MyExtensions {//类名不固定
        public static bool IsPalindrome(this string str){
            for(int i = 0, j = str.Length-1; i < j; i++, j--)
                if(str[i] != str[j])
                    return false;
            return true;
        }
		
        public static int ReverseDigits(this int num){//方法内num值的修改不会影响外部值
			int Reverse_num = 0;
			while(num!=0){
				Reverse_num *= 10;
				Reverse_num += num%10;
                num /= 10;
			}
            return Reverse_num;
        }
    }
	
    class Program{
        static void Main(string[] args){
            Console.Write("Enter a string: ");
            string str = Console.ReadLine();
            Console.WriteLine("\"" + str + "\"" + (str.IsPalindrome() ? " is " : " is not ") + "a palindrome");
            
			Console.Write("Enter an integer: ");
            int a = int.Parse(Console.ReadLine());
            Console.WriteLine("The reverse of " + a.ReverseDigits() + " is " + a);
			
            Console.ReadKey();
        }
    }
}
csc Program.cs

构造函数和析构函数

对象初始化和回收对象资源。

构造函数

构造方法,类的特殊成员方法,用来完成类的成员变量的自动初始化。
若编写了构造函数,系统不会提供默认构造函数。

  1. 无参构造函数
[访问修饰符] <类名>(){
	构造函数的主体
}
class Employee{
	public Employee(){}
}
  1. 有参构造函数
[访问修饰符] <类名>([参数列表]){
	构造函数的主体
}
class Employee{
	private string name;
	public Employee(string n){ 
		name = n;
	}
}

析构函数

垃圾回收机制,当类实例不再有效,并符合析构条件时,会调用该类的析构函数。

~<类名>(){
	析构函数主体
}

封装性

概述

面向对象编程语言中,编程单元是类,通过类封装数据和操作,类对外提供接口来访问和操作。
封装(Encapsulation)用于对外部进行隐藏类的内部。
支持封装的修饰符:

  • private
  • protected
  • internal
  • protected internal
  • public
  • other encapsulating strategy(其他封装策略):属性和索引器。

属性

属性提供将对象的读写和操作关联起来的机制,是一种特殊方法(访问器)。

class Person{
	private string name;
	private int age;
	public void SetName(string n){
		this.name = n;
	}
	public void SetAge(int a){
		this.age = a;
	}
	public string GetName(){
		return this.name;
	}
	public int GetAge(){
		return this.age;
	}
}

string name;
int age;
Person p = new Person();
p.SetName("ma");
p.SetAge(30);
name = p.GetName();
age = p.GetAge();
class Person{
	private string name;
	private int age;
	public string Name(){
		set{this.name = value;}
		get{return this.name;}
	}
	public int Age(){
		set{this.age = value;}
		get{return this.age;}
	}
}

string name;
int age;
Person p = new Person();
p.Name = "ma";
p.Age = 30;
name = p.Name;
age = p.Age;
  • 属性是像类的字段一样的访问方法。
  • 属性只能包含一个get访问器和一个set访问器,不能含其他方法、字段等。
  • get访问器返回属性类型值。

索引器

索引器用于简化类中数组或集合成员的存取操作。

[修饰符] 数据类型 this[索引类型 index] {
	get{//获得属性的代码}
	set{//设置属性的代码}
}
public class ID{
	private string[] name = new string[2];
	public string this[int index] {
		get{return name[index];}
		set{name[index] = value;}
	}
}

string name;
ID id = new ID();
id[0] = "ma";
id[1] = "hong";

name = id[0];

索引器与数组的区别:

  • 数组索引值为整数,索引器索引值(Index)不限定为整数。
  • 索引器允许重载。
  • 索引器不是变量,未定义直接存储数据的内存,数组有。索引器有get和set访问器。

索引器与属性的区别:

  • 索引器以方法签名(this)标识,属性采用名称标识。
  • 索引器可以重载,属性不可以。
  • 索引器不能用static声明(索引器属于实例成员),属性可以。

类的继承

概述

继承已有类的成员(数据和方法),方便创建和维护程序,重用代码有利于节省开发时间。
已有类为基类,继承类为派生类。

<访问修饰符> class<基类名称> {
	基类主体部分
}

class <派生类>:<基类名称> {
	派生类主体部分
}
public class Car{
	private string type;
	public string Type(){
		set{this.type = value;}
		get{return this.type;}
	}
}

public class ChineseCar:Car{
	private string nation;
	public string Nation(){
		set{this.nation = value;}
		get{return this.nation;}
	}
}

string type;
string nation;
ChineseCar car = new ChineseCar();
car.Type = "type1";
car.Nation = "China";
type = car.Type;
nation = cat.Nation;
  • 构造函数和析构函数不能被继承
  • 派生类中同名成员会覆盖基类同名成员(不会删除)。
  • 基类定义虚方法、虚属性及虚索引器,派生类重载,从而实现多态。
  • 派生类只能单一继承,接口可实现多重继承。

base使用

base用于派生类中访问基类公有或保护成员的访问,只局限在构造函数,实例方法和实例属性访问器中。

  • base调用基类构造方法;
class Car(){
	public string type;
	public Car(){
		car = "货车";
	}
}

class ChineseCar:Car{
	public string nation;
	public ChineseCar():base(){
		nation = "中国";
	}
}
  • base在派生类中调用基类方法。
class Car(){
	public void AddCar(){
	}
}

class ChineseCar:Car{
	public void AddTypee(){
		base.AddCar();
	}
}

多层继承中:

  • 重载存在,base指向直接继承的父类成员的方法;
  • 无重载存在,base指向任何上级父类的公有或保护方法。

多态

概述

同一操作作用于不同的对象,可以有不同的解释,产生不同的执行结果。

  • 编译时多态:重载实现,非虚成员,系统在编译时,根据传递的参数、返回类型等信息决定操作。
  • 运行时多态:重写虚函数实现,系统运行时,根据实际情况决定操作。

实现多态的方式

  • 多个类继承同一个类,继承实现多态,派生类重写基类成员。
  • 抽象类被派生类扩充使用,抽象类实现多态,抽象类中部分或全部未实现的成员在派生类中全部实现;抽象类中已实现成员仍可被重写。
  • 多个类实现相同接口,接口实现多态。

虚方法与重写

虚方法重载,派生类中实现对基类虚方法的重新定义。

声明虚方法:

[访问修饰符] virtual [返回类型] ([参数列表]) {
	虚方法实现部分
}

派生类中覆盖同名方法使用override。

public class oblong{
	protected double x, y;
	public const double p = Math.PI;
	public oblong(double x1, double y1){
		x = x1;
		y = y1;
	}
	public virtual double Area(){
		return x*y;
	}
}

public class round:oblong{
	public round(double r):base(r, 0){}
	public override double Area(){
		return p*x*x;
	}
}

round r = new round(2);
r.Area();

oblong ob = new oblong(2,3);
ob.Area();

派生类中创建同名方法使用new。

public class oblong{
	protected double x, y;
	public const double p = Math.PI;
	public oblong(double x1, double y1){
		x = x1;
		y = y1;
	}
	public virtual double Area(){
		return x*y;
	}
}

public class round:oblong{
	public round(double r):base(r, 0){}
	public new double Area(){
		return p*x*x;
	}
}

round r = new round(2);
r.Area();

oblong ob = new oblong(2,3);
ob.Area();

override和new的相同点是定义与基类相同的方法,派生类对象执行各自的派生类中的方法;
不同点是派生类对象向上转型后,重写(override)基类调用派生类方法,隐藏(new)基类调用基类方法。

抽象类与抽象方法

抽象类是不能被实例化的类(至少含有一个抽象方法,抽象方法必须实现),只能用来派生类。
抽象方法只能声明,派生类中实现。

[访问修饰符] abstract class 类名{
	//抽象类成员定义
}

[访问修饰符] abstract void 方法名(方法参数);
public abstract class oblong{
	protected double x, y;
	public const double p = Math.PI;
	public oblong(double x1, double y1){
		x = x1;
		y = y1;
	}
	public abstract double Area();
}

public class round:oblong{
	public round(double r):base(r, 0){}
	public override double Area(){ //override或new都行
		return p*x*x;
	}
}

public class retangle:oblong{
	public retangle(double l, double w):base(l, w){}
	public override double Area(){ //override或new都行
		return x*y;
	}
}

round r = new round(2);
r.Area();

rectangle ra = new rectangle(2,3);
ra.Area();

//错误
//oblong b = new oblong(2,3);

猜拳游戏

Program.cs

using System;

namespace Game{
    class Program{
        static void Main(string[] args){
            Player p1 = new Player(){ Name = "Tony" };
            Computer c1 = new Computer();
            Judge j1 = new Judge();
            while(true){
                int res1 = p1.ShowFist();
                int res2 = c1.ShowFist();
                j1.Determine(res1, res2);
                Console.ReadKey();
            }
        }
    }
}

Player.cs

using System;

namespace Game{
    class Player{
        string name;
        public string Name{
            get { return name; }
            set { name = value; }
        }

        public int ShowFist(){
            Console.WriteLine("请问,你要出什么拳?  1.剪刀     2.石头    3.布");
            int result = ReadInt(1, 3);
            string fist = IntToFist(result);
            Console.WriteLine("玩家{0}出了{1}", name, fist);
            return result;
        }
        private string IntToFist(int input){
            string result = string.Empty;

            switch(input){
			case 1:result = "剪刀";
				break;
			case 2:result = "石头";
				break;
			case 3:result = "布";
				break;
            }
            return result;
        }
        private int ReadInt(int min, int max){
            while(true){
                //从控制台获取用户输入的数据
                string str = Console.ReadLine();

                //将用户输入的字符串转换成Int类型
                int result;
                if(int.TryParse(str, out result)){
                    //判断输入的范围
                    if(result >= min && result <= max){
                        return result;
                    }else{
                        Console.WriteLine("请输入1个{0}-{1}范围的数", min, max);
                        continue;
                    }
                }else{
                    Console.WriteLine("请输入整数");
                }
            }
        }
    }
}

Computer.cs

using System;

namespace Game{
    class Computer{  
		//生成一个随机数,让计算机随机出拳
		Random ran = new Random();
        public int ShowFist(){
            int result = ran.Next(1, 4);
            Console.WriteLine("计算机出了{0}", IntToFist(result));
            return result;
        }
        private string IntToFist(int input){
            string result = string.Empty;

            switch(input){
			case 1:result = "剪刀";
				break;
			case 2:result = "石头";
				break;
			case 3:result = "布";
				break;
            }
            return result;
        }
    }
}

Judge.cs

using System;

namespace Game{
    class Judge{
        public void Determine(int p1, int p2){
            if(p1 - p2 == -2 || p1 - p2 == 1){
                Console.WriteLine("玩家胜利!");
            }else if(p1 == p2){
                Console.WriteLine("平局!");
            }else{
                Console.WriteLine("玩家失败!");
            }
        }
    }
}
csc Program.cs Player.cs Computer.cs Judge.cs

系统登录用户类

Program.cs

using System;

namespace myGame{
    class Program{
        static void Main(string[] args){
            gameUser guer1 = new gameUser("剑士", "123456");
            Console.WriteLine("玩家:" + guer1.Username + ", 密码:" + guer1.Password);
            Console.Read();
        }
    }
}

gameUser.cs

namespace myGame{
    class gameUser{
        private string username;
        public string Username{
            get { return username; }
            set { username = value; }
        }
        private string password;
        public string Password{
            get { return password; }
            set { password = value; }
        }
        public gameUser(){}
        public gameUser(string username,string password){
            this.username = username;
            this.password = password;
        }
    }
}
csc Program.cs gameUser.cs

接口

C#类不支持多重继承,通过接口实现多重继承。

接口概念

接口是契约、标准,只能定义方法、属性、索引器和事件等成员(不能实现),与抽象类类似(但接口所有成员完全抽象)。
类可以继承多个接口,接口可以继承接口,类可以通过继承基类(或接口)多次继承同一个接口。

接口特点:

  • 类似抽象基类,继承接口的非抽象类必须实现接口。
  • 接口不能直接实例化。
  • 接口可包含事件、索引器、方法和属性。
  • 接口不包含方法的实现。
  • 类和结构可从多个接口继承。
  • 接口自身可从多个接口继承。

接口声明

修饰符 interface 接口名称:继承的接口列表{
	接口内容;
}

接口中成员默认public,不允许添加访问修饰符。

interface Information{
	string Code{get;set;}
	string Name{get;set;}
	void ShoowInfo();
}

接口应用

账户接口1,所有银行账户类都要继承此接口,基本功能。

public interface IBankAccount{
	void PayIn(decimal amount); //存钱
	bool Withdraw(decimal amount);//取钱
	decimal Balance{get;}//余额
}

普通账户类1

public class SaverAccount:IBankAccount{
	private decimal balance;
	public void PayIn(decimal amount){
		balance += amount;
	}
	public bool Withdraw(decimal amount){
		if(balance>=amount){
			balance -= amount;
			return true;
		}else{
			Console.WriteLine("余额不足!");
			return false;
		}
	}
	public decimal Balance{get{return balance;}}
	public override string string ToString(){
		return String.Format("Saver Bank balance:", balance);
	}
}

账户接口2,包含高级银行账户的一些额外功能。

public interface IBankAdvancedAccount{
	void DealStartTip(); //交易开始提示
	void DealStopTip();//交易结束提示
}

金卡账户类2

public class GoldAccount:IBankAccount,IBankAdvancedAccount{
	private decimal balance;
	public void PayIn(decimal amount){
		balance += amount;
	}
	public bool Withdraw(decimal amount){
		if(balance>=amount){
			balance -= amount;
			return true;
		}else{
			Console.WriteLine("余额不足!");
			return false;
		}
	}
	public decimal Balance{get{return balance;}}
	public override string string ToString(){
		return String.Format("Saver Bank balance:", balance);
	}
	public void DealStartTip(){
		Console.WriteLine("交易开始,请注意周围环境!");
	}
	public void DealStopTip(){
		Console.WriteLine("交易结束,请带好你的贵重物品,欢迎下次光临!");
	}
}

委托

实现方法的参数化,委托是一种引用方法的类型,类似函数指针的集合,为委托分配了多个方法(依序可重复),调用委托会一次调用分配的方法。

委托定义

委托是类,定义了方法的类型,可以将方法当作参数传递,避免了大量if…else…(switch)语句,可扩展性。
委托与函数指针的区别:

  • 安全性:c/c++函数指针只是传递函数地址,无类型安全性;.NET中委托类型安全。
  • 与实例关联性:方法通常与类实例关联,委托可以获取类实例信息,从而实现与类实例的关联。
  • 函数指针是指针变量,分配在栈中;委托声明是类,类实例化为对象,分配在堆中。
  • 委托可以指向不同类中相同类型的方法,函数指针不可以。

委托声明

[修饰符] delegate [返回值类型] [委托名称] ([参数列表])

[修饰符]可选。

委托是一种数据类型,派生于System.Delegate的派生类。

委托应用

Test.cs

using System;

class Test{
	delegate int MyDelegate(int x, int y);//x,y不能省略
	int Add(int x, int y){
		return x+y;
	}
	static void Main(){
		Test tc = new Test();
		MyDelegate md = tc.Add;
		Console.WriteLine(md(2,3));//等价于md.Invoke(2,3)
	}
}
csc Test.cs

Heater.cs

using System;
using System.Threading;

class Heater{
	int temperature;
	string type = "RealFire 001";
	string area = "China CQ";
	
	public class BoiledEventArgs:EventArgs{
		public readonly int temperature;
		public BoiledEventArgs(int temperature){
			this.temperature = temperature;
		}
	}
	
	//声明委托
	delegate void BoiledEventHandler(Object sender, BoiledEventArgs e);
	
	//声明事件
	event BoiledEventHandler Boiled;
	
	
	protected virtual void OnBoiled(BoiledEventArgs e){
		if(Boiled!=null)
			Boiled(this, e);
	}
	
	void BoilWater(){
		for(int i=0;i<=100;i++){
			temperature = i;
			if(temperature>95){
				BoiledEventArgs e = new BoiledEventArgs(temperature);
				OnBoiled(e);
			}
		}
	}
	
	class Alarm{
		public void MakeAlert(Object sender, BoiledEventArgs e){
			Heater heater = (Heater)sender;
			Console.WriteLine("Alarm:{0}-{1}", heater.area, heater.type);//内部类可以访问外部内私有变量
			Console.WriteLine("Alarm:嘀嘀嘀,水已经{0}℃了", e.temperature);
			Console.WriteLine();
		}
	}
	
	class Display{
		public static void ShowMsg(Object sender, BoiledEventArgs e){
			Heater heater = (Heater)sender;
			Console.WriteLine("Display:{0}-{1}", heater.area, heater.type);//内部类可以访问外部内私有变量
			Console.WriteLine("Display:水快烧开了,当前温度:{0}℃", e.temperature);
			Console.WriteLine();
			Thread.Sleep(1000);
		}
	}
	
	class Program{
		static void Main(){
			Heater heater = new Heater();
			Alarm alarm = new Alarm();//内部类可以创建私有内部类,访问私有内部类的方法需要public
			heater.Boiled = alarm.MakeAlert;//给事件赋值方法
			//heater.Boiled += alarm.MakeAlert;//Boiled为null,与上面等价
			heater.Boiled += (new Alarm()).MakeAlert;//匿名对象注册方法
			heater.Boiled += new BoiledEventHandler(alarm.MakeAlert);//通过委托对象注册方法
			heater.Boiled += Display.ShowMsg;//注册静态方法
			heater.BoilWater();//烧水,自动调用注册过的方法
		}
	}
}
csc Heater.cs

匿名方法

匿名方法允许一个与委托关联的代码被内联地写入使用委托的位置,可以共享对本地语句包含的方法成员的访问。

delegate(【参数列表】){
	【代码块】
}

Program.cs

using System;
using System.Threading;

class Program{
	delegate void DelOutput(string s);
	static void NamedMethod(string k){
		Console.WriteLine(k);
	}
	static void Main(){
		DelOutput del = delegate(string j){
			Console.WriteLine(j);
		};
		del.Invoke("匿名方法被调用");
		Console.WriteLine();
		
		del = NamedMethod;
		del.Invoke("静态方法被调用");
		Console.WriteLine();
	}
}
csc Program.cs

事件

事件定义

事件使用

目录与文件管理

这篇关于C#基础教程二的文章就介绍到这儿,希望我们推荐的文章对大家有所帮助,也希望大家多多支持为之网!