C#学习笔记---基础入门(二)
枚举
枚举是被命名的整型常数的集合;枚举类型的变量只有赋值后才能使用;不同枚举中的枚举值可以重名;可以自定义枚举值。
enum Playstates {
跑, 跳,下滑,左转,右转
}枚举值的使用:Playstates.跑;
结构体
结构体相当于一个复合型的容器,其是由一系列不同类型的数据构成的集合;结构体中的成员不能在定义中赋初始值。
struct Person{
//public 类型 变量名
public string name;
public int age;
}//在main函数之前定义结构体的使用:Person p;//先声明一个person类型变量
p.name="老王";
访问修饰符
public 公共,访问不受限制
private 私有,只能在当前的类或者结构体中使用
protected 受保护的 只有在其所在类或者子类中可以访问,不能在结构体中使用
internal 内部的,同一程序集中的任何代码都可以访问该类型或成员,但在其他程序集中不可以访问
权限:1没有嵌套在其他类或者结构中的类型,只能是public或者internal,默认是internal
2 类成员可以是任意的访问权限,不加修饰符默认是private
类与对象
类是抽象的类型,对象是具体的实现
访问修饰符 class 类名{
类成员...
}
public class Person{public string name;public int age;}
Person p=new Person();//使用new关键字声明一个新的对象,对象也叫做类的实例。
字段
我们用字段描述类的特征 ,在类中可以直接给字段设置初始值;
访问字段:p.name="老王";
方法
我们用方法描述类的行为,方法就是在类中定义的多条语句的代码块
语法 :访问修饰符 返回值类型 方法名(参数列表){语句;..;return 返回值;}
调用方法:对象.方法名(参数);
对于私有成员变量既不能获取也不能更改,为了获得,可以设置get,set方法
public class Car { private string name="老王";//私有变量不能被外界访问 public string GetName() { return name; } public void SetName(string newname) { name = newname; } }
属性
属性自动帮我们给字段添加set get方法,属性本质上也是set get方法,只是形式不同
语法: 访问修饰符 属性类型 属性名 {set{};get{}}
public class Car { private string name="老王"; public string Name//属性 { //get访问器 get { return name; } //set访问器 set { //value关键字只能在属性的set方法中有意义,表示外界客观传递过来的值 name = value; } } }
使用:Car C=new Car();
C.Name="hello";//更改名称,注意使用属性名Name
属性的访问权限:仅当属性同时具有Set与Get方法时,才能使用访问器修饰符,并且只能够对其中一个访问器使用修饰符
值类型和引用类型
值类型在栈中分配空间,由系统自动分配
引用类型在堆中分配空间,由我们用new分配
string是特殊的引用类型
方法参数
形参:定义方法时,在参数列表中定义的参数叫做形参
实参:调用方法时,在参数列表中传递的参数叫做实参
值类型在方法内对形参做改变,不会影响到外界的实参,而引用类型可以在方法内对外界值做改变。
值类型的参数想要达到引用类型参数的效果,需要用到引用修饰符ref
public void Swap(ref int x, ref int y){
int temp=x;
x=y;
y=temp;
}
int i=5;
int j=12;
Swap(ref int i,ref int j);//可实现两数互换值。
输出参数:如果想让一个方法返回多个值,在参数前加out修饰符
public void Sum(int i,int j,out int sum){
sum=0;//必须先在方法中赋初始值
sum=i+j;
}
int r;
Sum(5,13,out r);
可变参数:如果在定义方法时不能确定有多少个参数,可以使用可变参数params
public int Sum(params int[] nums){
int s=0;
foreach(int n in nums){ s+=n;}
return s;
}
int m= Sum(1,2,3,4,5);
字符串
string字符串是引用类型,因为它本质上是一个char类型的数组。因此可以使用下标索引字符串中的字符元素
方法:Contains();//判断是否包含子字符串
IndexOf();//获取指定字符或者字符串中的下标
Remove();//删除子字符串
Replace();//替换子字符串
Split();//分割子串
Substring();//获取字串
string str = "lanou/jiaoyu/hello/haha 123"; string[] a = str.Split(new char[] { '/' ,' '}, 5); foreach(string tem in a) { Console.WriteLine(tem); } Console.ReadLine();//输出5个子字符串
方法重载
即多个不同的方法采用同样的名字——方法名相同,参数列表不同(参数类型不同或者参数个数不同)
这样可以使得方法调用更为方便。
在方法重载中与返回值类型无关
递归
递归---在方法体内,再次调用方法本身,递归必须有出口
例如数学公式计算从1加到100的和
f(n)=f(n-1)+n ; //当n>1
f(n)=1;//当n=1
转化为递归表达为
public class AB { public int F(int n) { if (n > 1){ return F(n - 1) + n; } else{ return 1; } } } static void Main(string[] args){ AB a = new AB(); Console.WriteLine(a.F(5)); Console.ReadLine(); }
递归方法的使用适合那些复杂的大问题可以拆分为与原问题相同的小问题来解决,并且小问题更为简单。
构造与析构
public class Person { public string name; public int age; //构造方法负责初始化对象 //构造方法的方法名必须与类名一致 //构造方法没有返回值,不需要写Void //构造方法可以带有参数,因此可以重载构造方法 //如果没有给类手动添加构造方法,系统会默认添加构造方法 //构造方法可以设为私有,此时不能使用构造创建实例 public Person() { //初始化成员变量 name = "老王"; age = 20; } public Person (string name,int age) { this.name = name;//this指向类中的字段 this.age = age; }
//析构方法--在对象销毁时调用,用于释放内存
//1.每个类中只有一个析构方法
//2.析构函数不能有返回值
//3.析构函数不能有访问权限修饰符
//4.析构函数不能带有参数,更不能重载
//5.析构函数由系统自动调用,不能手动调用
~Person(){
} } static void Main(string[] args) { //使用new关键字创建对象的时候就已经调用了构造方法 Person p = new Person(); Console.WriteLine(p.name); }
面向对象
封装与继承
封装——封装是实现面向对象程序设计的第一步,封装就是将数据/方法等集合在一个个单元中,称之为类,封装的意义在于保护代码/数据,屏蔽复杂性
继承——一个类可以继承于另外一个类,被继承的类叫做父类,或者基类,继承的类叫做子类,或者派生类。
(C#只支持单继承,也就是一个子类只能有一个父类。继承具有传递性)
public class Student:Person{//student继承于Person类,则拥有了person的属性,方法等,同时可以进行功能扩展
}
抽象类
父类中添加抽象方法需要添加abstract关键字
例如 public abstract void Eat();//在父类中定义的抽象方法不能够实现//抽象方法只能出现在抽象类中,则类前也需要加abstract
子类中实现父类的抽象方法时,需要添加override关键字
抽象类不能够直接实例化,即不能通过抽象类直接创建对象
抽象类中可以包含普通的方法
抽象方法不能使用private访问权限修饰符
静态类
用static修饰的成员是静态成员
静态成员只能由类来调用,不能使用对象或者实例调用
用static修饰的类是静态类
静态类不能实例化,只能包含静态成员和const常量
在内存中一共存有五个区域
1.堆区:需要程序员手动开辟并管理内存
2.栈区:由系统自动完成内存管理
3.静态区(全局区):程序运行过程中内存中的数据一直存在
4.常量区:用于存放常量
5.代码区:存放程序运行所需的代码
静态构造方法
构造方法也可以设置为静态
静态构造不能有参数和访问修饰符
不管是不是静态类,都可以有静态构造
静态构造会在第一次实例化或调用静态成员时调用
单例
单例是一种设计模式,确保一个类最多同时只存在一个实例,并易于访问
class Program { //单例保证在程序运行期间,一个类最多同时存在一个唯一的对象,访问对象更加方便 public class Player { //玩家角色属性 public string name; public int level; public int hp; public int maxhp; //1.私有化构造方法,不让外界随意创建对象 private Player() { } //2.需要在类的内部提供一个静态实例 private static Player _instance; //3.提供获取实例的接口 public static Player GetInstance() { if(null==_instance) { _instance = new Player(); } return _instance; } } public class Bag { public void UseHp() { Player p = Player.GetInstance(); p.hp += 10; } } static void Main(string[] args) { Player p = Player.GetInstance(); p.name = "aa"; p.level = 2; p.hp = 30; p.maxhp = 100; Bag b = new Bag(); b.UseHp(); Console.WriteLine(p.hp ); Console.ReadLine(); } }
虚方法
public void A() { Console.WriteLine("这个方法是在Super中定义的A"); } //虚方法,使用virtual关键字 //只有方法与属性可以是虚,字段不能是虚 //抽象方法与虚方法的不同: //1.抽象方法必须在抽象类中 //2.抽象方法在父类中不能实现 //3.抽象方法在非抽象子类中必须实现 public virtual void B() { Console.WriteLine("这个方法是在Super中定义的B"); } } public class sub:Super { //方法替换,——使用new关键字进行在子类中重新实现父类中定义的方法 public new void A() { Console.WriteLine("这个方法是在sub中定义的A"); } public override void B() { Console.WriteLine("这个方法是在sub中定义的B"); } } static void Main(string[] args) { //使用父类类型引用父类对象 Super super = new Super(); super.A();//调用了父类中定义的方法 super.B();//调用了父类中定义的方法 //使用子类类型引用子类对象 sub sub = new sub(); sub.A();//调用了子类中定义的方法 sub.B();//调用了子类中定义的方法 //使用父类类型引用子类对象 Super c = new sub(); c.A();//调用了父类中定义的方法 c.B();//调用了子类中定义的方法 //多态的概念就是--使用父类类型调用子类中实现的方法 Console.ReadLine(); }
接口
interface
public class A { } //实物接口 public interface Food { //在接口中定义方法 //1.不能添加访问修饰符,默认都是public //2.在接口中的方法不能实现 //3.接口中可以定义属性与方法,不能定义字段 float Price { get; }//定义属性 void Eat(); } //Apple继承了A类,并实现了Food接口 //一旦某个类实现了接口,就必须实现接口中定义的全部成员 public class Apple :A, Food { public float Price { get { return 3.4f; } } public void Eat() { Console.WriteLine("吃苹果Hp+10"); } } static void Main(string[] args) { Apple a = new Apple(); a.Eat(); Console.WriteLine(a.Price); //多态-使用接口实现多态 使用多态(抽象,虚方法,以及接口) Food b = new Apple(); b.Eat(); Console.WriteLine(b.Price); //不能直接实例化接口即Food b = new Food(); Console.ReadLine(); }