//题目3:
Console.WriteLine("请按语文数学英语的顺序,输入三门成绩:");
Console.WriteLine("输入语文成绩:");
int chinese = Convert.ToInt32(Console.ReadLine());
//或者:int chinese = int.Parse(Console.ReadLine());
Console.WriteLine("输入数学成绩:");
int math = Convert.ToInt32(Console.ReadLine());
Console.WriteLine("输入英语成绩:");
int english = Convert.ToInt32(Console.ReadLine());
Console.WriteLine("Chinese: {0}\nMath: {1}\nEnglish: {2}",chinese,math,english);
//或者:Console.WriteLine("Chinese:"+chinese+"\n"+"Math:"+math+"\n"+"English:"+english);
char c = 'a';
const char c2 = 'a';
switch(c) // switch语句
{
case c2:
Console.WriteLine("c等于c2");
break;
}
贯穿
如果某几个case所要执行的语句一样,可以只在最后一个case中写即可
csharp复制代码
int c = 0;
switch(c) // switch语句
{
case 1:
case 2:
case 3:
Console.WriteLine("哈哈哈");
break;
case 4:
Console.WriteLine("呵呵呵");
break;
default:
Console.WriteLine("啥也没有");
break;
}
//题目:求100以内的素数
int num = 2;
while(num < 100){
bool isPrime = true;
for(int i = 2; i < num; i++){
if(num % i == 0){
isPrime = false;
break;
}
else{
isPrime = true;
}
}
if(isPrime) Console.WriteLine(num);
num++;
}
do while
先斩后奏,先执行一次循环体,再判断是否继续
注意:while后面有分号
csharp复制代码
//do while 循环
do{
Console.WriteLine("do while 循环");
}while(false);
do while中的continue和break
continue是回到while的条件语句
csharp复制代码
//do while 循环
do{
Console.WriteLine("do while 循环");
continue;
}while(false);
结果和上面一样
for循环
continue和break的用法和while一样,所以需要注意配套使用,不能跨级使用
和while的区别:
for循环可以用来准确的到一个范围内的所有数
习题:
经典水仙花数
csharp复制代码
//水仙花
int ge = 0, shi = 0, bai = 0, num = 0;
for(; num <= 999; num++){
bai = num / 100;
shi = num % 100 / 10;
ge = num % 10;
if(num == ge * ge * ge + shi * shi * shi + bai * bai * bai){
Console.WriteLine(num);
}
}
#region 1
int[] arr1 = new int[100];
for(int i = 0; i < arr1.Length; i++){
arr1[i] = i;
}
Console.WriteLine(string.Join(",", arr1));
#endregion
csharp复制代码
#region 2
int[] arr2 = new int[100];
for(int i = 0; i < arr2.Length; i++){
arr2[i] = arr1[i] * 2;
}
Console.WriteLine(string.Join(",", arr2));
#endregion
csharp复制代码
#region 3
Random r1 = new Random();
int[] arr3 = new int[10];
for(int i = 0; i < arr3.Length; i++){
arr3[i] = r1.Next(0, 101);
}
Console.WriteLine(string.Join(",", arr3));
#endregion
csharp复制代码
#region 4
Random r = new Random();
int[] arr = new int[10];
for(int i = 0; i < arr.Length; i++){
arr[i] = r.Next(0, 101);
}
Console.WriteLine("原数组:"+string.Join(",", arr));
//MAX MIN
int max = arr[0];
int min = arr[0];
int sum = 0;
for(int i = 0; i < arr.Length - 1; i++){
max = (max >= arr[i])? max : arr[i];
min = (min <= arr[i])? min : arr[i];
sum += arr[i];
}
Console.WriteLine("最大值:"+max);
Console.WriteLine("最小值:"+min);
Console.WriteLine("和:"+sum);
double avg = (double)sum / arr.Length;
Console.WriteLine("平均值:"+avg);
#endregion
csharp复制代码
#region 5
Random r2 = new Random();
int[] arr4 = new int[10];
for(int i = 0; i < arr4.Length; i++){
arr4[i] = r2.Next(0, 101);
}
Console.WriteLine("原数组:"+string.Join(",", arr4));
for(int i = 0; i < arr4.Length /2; i++){
arr4[i] = arr4[i] + arr4[arr4.Length-1-i];
arr4[arr4.Length-1-i] = arr4[i] - arr4[arr4.Length-1-i];
arr4[i] = arr4[i] - arr4[arr4.Length-1-i];
}
Console.WriteLine("反转后:"+string.Join(",", arr4));
#endregion
csharp复制代码
#region 6
Random r3 = new Random();
int[] arr5 = new int[10];
for(int i = 0; i < arr5.Length; i++){
arr5[i] = r3.Next(-100, 101);
}
Console.WriteLine("原数组:"+string.Join(",", arr5));
for(int i = 0; i < arr5.Length; i++){
if(arr5[i]>0) arr5[i]++;
else if(arr5[i]<0) arr5[i]--;
}
Console.WriteLine("变化后:"+string.Join(",", arr5));
#endregion
csharp复制代码
#region 7
int[] arr6 = new int[10];
//输入
try{
for(int i = 0; i < arr6.Length; i++){
Console.Write("请输入第{0}个元素:", i+1);
arr6[i] = Convert.ToInt32(Console.ReadLine());
}
}
catch{
Console.WriteLine("输入有误,程序退出");
}
Console.WriteLine("原数组:"+string.Join(",", arr6));
//MAX MIN
int max = arr6[0];
int min = arr6[0];
int sum = 0;
for(int i = 0; i < arr6.Length - 1; i++){
max = (max >= arr6[i])? max : arr6[i];
min = (min <= arr6[i])? min : arr6[i];
sum += arr6[i];
}
Console.WriteLine("最大值:"+max);
Console.WriteLine("最小值:"+min);
Console.WriteLine("平均值:"+(double)sum / arr6.Length);
#endregion
csharp复制代码
#region 8
string[] arr7 = new string[25];
for(int i = 0; i < arr7.Length; i++){
arr7[i] = (i%2 == 0)? "■": "□";
}
for(int i = 0; i < arr7.Length; i++){
Console.Write(arr7[i]);
if((i+1)%5 == 0 && i!= 0){
Console.WriteLine();
}
}
#endregion
namespace 函数;
class Program
{
//有参有返回值的函数
static int[] sum_avg(int a, int b)
{
int sum = a + b;
int avg = sum / 2;
// int[] result = { sum, avg };
return new int[] { sum, avg };
}
static void Main(string[] args)
{
int a = 10;
int b = 20;
int[] result = sum_avg(a, b);
Console.WriteLine("The sum is: " + result[0]);
Console.WriteLine("The average is: " + result[1]);
}
}
#region 1
static int Max(int a, int b){
return (a > b)? a : b;
}
#endregion
csharp复制代码
#region 2
static float[] Circle(float r){
//1
// float area = 3.14f * r * r;
// float perimeter = 2 * 3.14f * r;
// float[] result = {area, perimeter};
// return result;
//2
return new float[] { 3.14f * r * r, 2 * 3.14f * r };
}
#endregion
csharp复制代码
#region 3
static int[] CalculateArr(int[] arr){
if( arr.Length == 0 )
{
Console.WriteLine("数组不能为空");
return new int[0];
}
int sum = 0, max = arr[0], min = arr[0], average = 0;
for (int i = 0; i < arr.Length; i++)
{
sum += arr[i];
max = (max >= arr[i]) ? max : arr[i];
min = (min <= arr[i]) ? min : arr[i];
}
average = sum / arr.Length;
int[] result = { sum, max, min, average };
return result;
}
#endregion
#region 1
static int[] Calculate(params int[] numbers){
int sum = 0;
for(int i = 0; i < numbers.Length; i++){
sum += numbers[i];
}
int average = sum / numbers.Length;
return new int[] {sum, average};
}
#endregion
#region 2
static int[] Sum_Odd_Even(params int[] numbers){
int sum_odd = 0;
int sum_even = 0;
for(int i = 0; i < numbers.Length; i++){
if(numbers[i] % 2 == 0){
sum_even += numbers[i];
}
else{
sum_odd += numbers[i];
}
}
return new int[] {sum_odd, sum_even};
}
#endregion
int[] result = Calculate(1, 2, 3, 4, 5);
Console.WriteLine("Sum: " + result[0]);
Console.WriteLine("Average: " + result[1]);
int[] result2 = Sum_Odd_Even(1, 2, 3, 4, 5);
Console.WriteLine("Sum of odd numbers: " + result2[0]);
Console.WriteLine("Sum of even numbers: " + result2[1]);
函数重载
函数名相同、参数的数量不同 (或 参数的数量相同,但参数的类型、顺序不同)的一组函数
作用:
用来命名一组功能相似的函数(不同参数的同一逻辑处理),减少函数名的数量,避免命名空间的污染
提高程序可读性
csharp复制代码
namespace 函数重载;
class Program
{
static int Sum(int a, int b)
{
return a + b;
}
//参数的数量不同
static int Sum(int a, int b, int c)
{
return a + b + c;
}
//参数的类型不同
static double Sum(double a, double b)
{
return a + b;
}
//参数的顺序不同(其实也是类型不同)
static float Sum(float a, int b){
return a + b;
}
//参数用ref out 修饰
//out传入的参数必须要在函数内部赋值
static int Sum(ref int a,out int b)
{
b = 1;
return a + b;
}
//参数是可变参数
static int Sum(params int[] nums)
{
int sum = 0;
for(int i = 0; i < nums.Length; i++)
{
sum += nums[i];
}
return sum;
}
static void Main(string[] args)
{
Sum(1, 2);
Sum(1, 2, 3);
Sum(1.0, 2.0);
Sum(1.0f, 2);
int a = 1;
int b;
Sum(ref a, out b);
}
}
习题
csharp复制代码
namespace 函数重载习题;
public class Program{
#region 1
static int Max(int a, int b){
return a > b? a : b;
}
static double Max(double a, double b){
return a > b? a : b;
}
#endregion
#region 2
static int Max(params int[] numbers){
int max = numbers[0];
for(int i = 0; i < numbers.Length; i++){
max = (numbers[i] > max)? numbers[i] : max;
}
return max;
}
static double Max(params double[] numbers){
double max = numbers[0];
for(int i = 0; i < numbers.Length; i++){
max = (numbers[i] > max)? numbers[i] : max;
}
return max;
}
#endregion
static void Main(string[] args)
{
Console.WriteLine(Max(1, 2));
Console.WriteLine(Max(1.0, 2.0));
Console.WriteLine(Max(1, 2, 3, 4, 5));
Console.WriteLine(Max(1.0, 2.0, 3.0, 4.0, 5.0));
}
}
namespace 面向对象;
class Person{
}
class Machine{
}
class Program
{
static void Main(string[] args)
{
#region 实例化对象示例(类创建对象)
//类对象都是引用类型的
//语法: 类名 对象名 = new 类名();
//在栈上开辟了一个空间存放地址,但是不开辟 堆内存空间,也就是null
Person p;
Person p1 = null;
//分配堆内存空间
//创建的每个对象只是模板都是同一个类,但是里面的信息都是不同的------------类似造人
Person p2 = new Person();
Person p3 = new Person();
#endregion
}
}
习题
csharp复制代码
namespace 类和对象习题;
class Person{
}
class Animal{
}
class Machine{
}
class Plant{
}
class Astro{
}
class Program
{
static void Main(string[] args)
{
// 1
Machine robot = new Machine();
Machine machine = new Machine();
Person people = new Person();
Animal cat = new Animal();
Person aunt = new Person();
Person uncle_Wang = new Person();
Machine car = new Machine();
Machine plane = new Machine();
Plant sunflower = new Plant();
Plant chrysanthemum = new Plant();
Astro sun = new Astro();
Astro star = new Astro();
Plant lotus = new Plant();
}
}
复制代码
A指向一个地址指向一块堆内存
B指向一个地址,地址拷贝自A的地址,所以也指向A的堆内存
B = null :把B的地址与堆内存之间的指向关系断开
所以,A的堆内存没变
A和B没关系
成员变量------类的特征
申明在类语句块中
用来描述对象的特征
任意变量类型
数量不限
赋不赋值都行
csharp复制代码
namespace 成员变量;
//性别枚举
enum E_SexType{
Male,
Female,
}
//位置结构体
struct Position{
}
//宠物类
class Pet{
}
class Person{
//特征------成员变量
public string name = "Eano";//可以初始化也可以不初始化
public int age;
public E_SexType sex;
public Position position;
//可以申明任意类的对象,包括自身类
// (这点和结构体就不同,结构体如果申明自身结构体的变量就会无限循环导致报错
// 而在类里申明自身类的对象则没有问题,因为类是引用类型,只是声明一个对该对象的引用,也就是开辟了一个地址空间
// 不能实例化自身类的对象,因为这样的话在后面创建对象的时候就会陷入无限循环)
public Person girlfriend; //不能实例化自身类的对象,初始化为null是可以的
public Person[] friends;
public Pet pet; //可以实例化其他类的对象
}
class Program
{
static void Main(string[] args)
{
//创建对象
Person p = new Person();
#region 成员变量的使用与初始值
//值类型的默认值 都是0
// 相应的bool------false , char------'' ,string------""
//引用类型的默认值 都是null
//调用defalut()方法可以查看默认值
Console.WriteLine(default(int));
Console.WriteLine(default(bool));
Console.WriteLine(default(char));
//如果不申明,那么这个成员变量就是默认值
Console.WriteLine(p.age);
p.age = 25;
Console.WriteLine(p.age);
#endregion
}
}
总结:
访问修饰符------3P
在类里面申明自身类的对象的时候,不能实例化
defalut()方法得到数据类型的默认值
习题
csharp复制代码
3P:
private
public
protected
csharp复制代码
namespace 成员变量习题;
class Student{
public string name;
public int age;
public string num;
public Student deskmate;
}
class Classroom{
public string major;
public int capacity;
public Student[] students;
public Classroom(int capacity)
{
this.capacity = capacity;
students = new Student[capacity];
}
}
class Program
{
static void Main(string[] args)
{
//3
Student s1 = new Student();
Student s2 = new Student();
//4
Classroom c1 = new Classroom(5);
}
}
namespace 成员方法;
class Person{
//成员方法
public void Speak(string message){
Console.WriteLine("{0}说{1}",name,message);
}
public bool IsAdult(){
return age>=18;
}
public void AddFriend(Person p){
if(friends==null) friends = new Person[]{p};
else{
Person[] temp = new Person[friends.Length+1];
for(int i=0;i<friends.Length;i++){
temp[i] = friends[i];
}
friends = temp;
friends[friends.Length-1] = p;
}
}
//成员变量
public Person[] friends;
public string name;
public int age;
}
class Program
{
static void Main(string[] args)
{
Person p = new Person();
p.Speak("Hello");
p.name = "Tom";
p.age = 20;
Console.WriteLine(p.IsAdult());
Person p2 = new Person(){name="Jerry",age=25};
p.AddFriend(p2);
Console.WriteLine(string.Join(",",p.friends.Select(f=>f.name)));
}
}
习题
csharp复制代码
namespace 成员方法习题;
class Student{
public void Speak(string message){
Console.WriteLine("{0} says: {1}",name,message);
}
public void Eat(Food food){
Console.WriteLine("{0} is eating {1},calories: {2}",name,food.name,food.calories);
}
public string name;
}
class Food{
public string name;
public int calories;
}
class Program
{
static void Main(string[] args)
{
Student student = new Student(){name="Alice"};
Food apple = new Food(){name="apple",calories=50};
student.Eat(apple);
}
}
构造、析构函数、垃圾回收机制
构造函数------初始化时调用
在类里面用于调用时快速初始化的函数
没有构造函数的时候默认存在一个无参构造函数
也就是 Person p = new Person();
写法:
和结构体一样,构造函数名要和类名相同
csharp复制代码
namespace 构造_析构函数;
class Person{
public string name;
public int age;
//构造函数
//类中允许申明无参构造函数,结构体则不允许
public Person(){
name = "eano";
age = 18;
}
//构造函数可以被重载
public Person(string name, int age){
this.name = name;
this.age = age;
}
}
class Program
{
static void Main(string[] args)
{
//现在有了3种申明并初始化对象的方式
Person p = new Person();
Console.WriteLine("Name: " + p.name);
Person p2 = new Person("eano", 18);
Console.WriteLine("Name: " + p2.name);
Person p3 = new Person(){name = "eano", age = 18};
Console.WriteLine("Name: " + p3.name);
}
}
注意:
有参构造函数 会顶掉 默认的无参构造函数。
想要保留无参构造函数,需要重载出来
this用来区分类内成员变量和外部传入参数
构造函数的特殊写法
:this(可选参数)复用代码
先进入无参构造函数
作用:复用先进入的构造函数代码
csharp复制代码
class Person{
public string name;
public int age;
//构造函数
//类中允许申明无参构造函数,结构体则不允许
public Person(){
name = "eano";
age = 18;
}
// //构造函数可以被重载
// public Person(string name, int age){
// this.name = name;
// this.age = age;
// }
//构造函数的特殊写法,在构造函数后:this(可选参数)
public Person(string name, int age) : this(){
Console.WriteLine("先进入无参构造函数");
}
public Person(string name, int age) : this(name){
Console.WriteLine("先进入string类型参数的构造函数");
}
}
:this(可选参数)可以指定先进入的构造函数
可选参数可以写死,比如
:this(int类型参数名)就是先进入参数为int类型的构造函数
:this(string类型参数名)就是先进入参数为string类型的构造函数
习题
csharp复制代码
namespace 构造_析构函数习题;
class Person{
public string name;
public int age;
//构造函数
public Person(){
name = "eano";
age = 25;
}
//重载
public Person(string name, int age){
this.name = name;
this.age = age;
}
//特殊的构造函数
public Person(string name):this(){
Console.WriteLine("有参构造函数里的name:"+name);
}
}
class Ticket{
uint distance;
float price;
//构造函数
public Ticket(uint distance){
this.distance = distance;
//price是通过GetPrice()方法计算出来的
price = GetPrice();
}
//成员方法
public float GetPrice(){
if(distance > 300){
return distance * 0.8f;
}
else if(distance > 200){
return distance * 0.9f;
}
else if(distance > 100){
return distance * 0.95f;
}
else{
return distance * 1.0f;
}
}
public void PrintPrice(){
Console.WriteLine("距离{0}的票价为:{1}",distance,GetPrice());
}
}
class Program
{
static void Main(string[] args)
{
//1
//先进入无参构造函数,再进入有参构造函数
Person p1 = new Person("John");
Console.WriteLine(p1.name+" "+p1.age);
//3
Ticket t1 = new Ticket(250);
t1.PrintPrice();
}
}
namespace 成员属性习题;
class Student{
private string name;
private string sex;
private int age;
private int csGrade;
private int unityGrade;
public string Name{get; private set;}
public string Sex{
get{
return sex;
}
private set{
if(value != "男" && value != "女") sex = "unknown";
else sex = value;
}
}
public int Age{
get{
return age;
}
private set{
if(value < 0) age = 0;
else if(value > 150) age = 150;
else age = value;
}
}
public int CsGrade{get; private set;}
public int UnityGrade{
get{
return unityGrade;
}
private set{
if(value < 0) unityGrade = 0;
else if(value > 120) unityGrade = 120;
else unityGrade = value;
}
}
public Student(string name, string sex, int age, int csGrade, int unityGrade){
Name = name;
Sex = sex;
Age = age;
CsGrade = csGrade;
UnityGrade = unityGrade;
}
public void Saymyself(){
Console.WriteLine("My name is {0}, I am {1} years old, a {2}.", Name, Age, Sex);
}
public void SayGrade(){
int sum = CsGrade + UnityGrade;
float average = (float)sum / 2;
Console.WriteLine("My sum grade is {0}, my average grade is {1}.", sum, average);
}
}
class Program
{
static void Main(string[] args)
{
Student student1 = new Student("Tom", "男", 18, 90, 80);
student1.Saymyself();
student1.SayGrade();
Student student2 = new Student("Jerry", "女", 160, 100, 90);
student2.Saymyself();
student2.SayGrade();
}
}
namespace 索引器;
class Person{
private string name;
private int age;
private Person[] friends;
private int[,] array;
public string Name{get;private set;}
public int Age{get;private set;}
public Person[] Friends{get;private set;}
public int[,] Array{get;private set;}
public Person(string name, int age){
Name = name;
Age = age;
friends = new Person[5];
Friends = friends;
array = new int[3, 4];
Array = array;
}
#region 索引器语法
//访问修饰符 返回值 this[数据类型 参数名1,数据类型 参数名2,...]{
// 和属性的写法相同:
// get{
// }
// set{
// }
// }
public Person this[int index]{
get{
#region 索引器里也能写逻辑
if(friends == null || index < 0 || index >= friends.Length){
return null;
}
else{
return friends[index];
}
#endregion
}
set{
if(friends == null){
friends = new Person[]{value};
}
//如果越界,顶掉最后一个元素
else if(index < 0 || index >= friends.Length){
friends[friends.Length - 1] = value;
}
else friends[index] = value;
}
}
#endregion
#region 索引器可以重载
//参数不同
public int this[int row, int col]{
get{
return array[row, col];
}
set{
array[row, col] = value;
}
}
public string this[string str]{
get{
switch(str){
case "name":
return Name;
case "age":
return Age.ToString();
default:
return "Invalid index";
}
}
}
#endregion
}
class Program
{
static void Main(string[] args)
{
Person p1 = new Person("Alice", 25);
p1.Friends[0] = new Person("Bob", 20);
p1[1] = new Person("Charlie", 22);
Console.WriteLine(p1[0].Name);
p1[2, 3] = 10;
Console.WriteLine(p1[2, 3]);
Console.WriteLine("{0}的年龄是{1}, 朋友是{2}", p1["name"],p1["age"],p1[0]["name"]);
}
}
索引器就相当于给对象加一个属性,用中括号[参数]调用这个属性的内容
习题
csharp复制代码
namespace 索引器习题;
class IntArray{
public int[] arr;
public int length;
public IntArray(int size){
length = 0;
arr = new int[size];
}
//增
public void Add(int index, int value){
if(index < 0 || index > length){
Console.WriteLine("索引超出范围");
return;
}
else{
if(length < arr.Length){
arr[length] = value;
length++;
}
else{
int[] newArr = new int[arr.Length + 1];
for(int i=0;i<arr.Length;i++){
newArr[i] = arr[i];
}
arr = newArr;
//后面元素后移
for(int i = length-1;i>=index;i--){
arr[i+1] = arr[i];
}
arr[index] = value;
length++;
}
}
}
//删
public void Remove(int index){
if(index > length-1 || index < 0){
Console.WriteLine("索引超出范围");
return;
}
else{
//后面元素前移
for(int i = index;i<length-1;i++){
arr[i] = arr[i+1];
}
length--;
}
}
//索引器
//查
//改
public int this[int index]{
get{
return arr[index];
}
set{
arr[index] = value;
}
}
}
class Program
{
static void Main(string[] args)
{
IntArray arr = new IntArray(5);
arr.Add(0,1);
arr.Add(1,2);
arr.Add(2,3);
arr.Add(3,4);
arr.Add(4,5);
arr.Add(5,6);
for(int i=0;i<arr.length;i++){
Console.Write(arr[i] + " ");
}
Console.WriteLine();
Console.WriteLine(arr.length);
arr.Remove(2);
for(int i=0;i<arr.length;i++){
Console.Write(arr[i] + " ");
}
Console.WriteLine();
Console.WriteLine(arr.length);
arr[0] = 10;
Console.WriteLine(arr[0]);
}
}
静态成员------类名.出来使用
静态关键字 static
修饰成员变量、方法、属性
静态成员可以用 类名.静态成员名直接调用
一般写成public公共的
申明与使用
csharp复制代码
namespace 静态成员;
class Test{
static public float PI = 3.14f;
public int testInt = 10;
static public float CircleArea(float r){
#region 静态函数不能访问非静态成员
// 非静态成员只能在实例化对象后调用
Test t = new Test();
Console.WriteLine(t.testInt);
#endregion
return PI * r * r;
}
public void TestFunc(){
Console.WriteLine("This is a test function");
#region 非静态函数可以使用静态成员
Console.WriteLine(PI);
Console.WriteLine(CircleArea(5));
#endregion
}
}
class Program
{
static void Main(string[] args)
{
#region 静态成员的使用
Console.WriteLine(Test.PI);
// Console.WriteLine(Test.testInt); // 不能直接类名.调用
// 非静态成员只能在实例化对象后调用
Test t = new Test();
Console.WriteLine(t.testInt);
Console.WriteLine(Test.CircleArea(5));
//Console.WriteLine(Test.TestFunc());// 不能直接类名.调用
t.TestFunc();
#endregion
}
}
为什么可以类名.静态成员名使用
程序开始运行的时候,就会给静态成员分配内存空间
静态成员与程序共生死
每个静态成员都会有一个唯一的内存空间
直到程序结束,静态成员的内存空间才会被释放
作用
申明唯一变量
方便在其他地方获取的对象的申明
申明唯一方法------相同规则的数学计算
问题
长期占用内存空间,其他非静态成员gc的阈值变小,程序性能降低
常态和静态变量
相同点:
都可以通过类名.出来使用
不同点:
const修饰常量,必须初始化,不能修改
const要直接写在变量的前面,也就是访问修饰符的后面
const只能修饰变量,static还可以修饰方法、属性
习题
csharp复制代码
namespace 静态成员习题;
//单例模式
class Test{
private static Test t = new Test();
public int testInt = 10;
public static Test T{
get{
return t;
}
}
private Test(){
}
}
class Program
{
static void Main(string[] args)
{
Console.WriteLine(Test.T.testInt);
Test.T.testInt = 20;
// Test t1 = new Test(); //外部无法实例化
Console.WriteLine(Test.T.testInt);
}
}
静态类和静态构造函数
作为工具使用,就像Console类一样,直接类名.出来使用静态成员
静态类
static修饰的类
只能包含静态成员
不能被实例化
作用:
将常用的静态成员写在静态类中
静态类不能被实例化,体现工具类的唯一性
静态构造函数
static修饰的构造函数
静态类和非静态类都可以用静态构造函数
静态构造函数不能使用访问修饰符
不能有参数
只会调用一次
静态构造函数只会在第一次使用类的时候调用一次,与类是否是静态类无关
普通构造函数每次实例化类的对象都会调用一次
csharp复制代码
using System.Runtime.CompilerServices;
namespace 静态类和静态构造函数;
#region 静态类
static class TestStatic{
public static void TestFunc(){
}
//静态类只能包含静态成员
// public void Say(){
// }
public static int TestIndex{get;set;}
}
#endregion
#region 静态构造函数
//1. 静态类中的静态构造函数
static class StaticClass{
public static int testInt = 10;
//静态构造函数不能加访问修饰符
//无参
static StaticClass(){
Console.WriteLine("静态类中的静态构造函数执行");
//在静态构造函数里初始化成员变量
testInt = 20;
}
}
//2. 普通类中的静态构造函数
class NormalClass{
public static int testInt = 10;
static NormalClass(){
Console.WriteLine("普通类中的静态构造函数执行");
//在静态构造函数里初始化成员变量
testInt = 20;
}
public NormalClass(){
Console.WriteLine("普通类中的普通构造函数执行");
}
}
#endregion
class Program
{
static void Main(string[] args)
{
//调用两次静态成员,但只执行一次静态构造函数
Console.WriteLine(StaticClass.testInt);
Console.WriteLine(StaticClass.testInt);
//普通类中的静态构造函数也只执行一次
Console.WriteLine(NormalClass.testInt);
Console.WriteLine(NormalClass.testInt);
//普通类中的普通构造函数每次实例化都会执行
NormalClass nc = new NormalClass();
NormalClass nc2 = new NormalClass();
}
}
习题
csharp复制代码
namespace 静态类和静态构造函数习题;
static class MathCalc{
const float pi = 3.14f;
public static float CircleArea(float r){
Console.WriteLine("半径为{0}的圆的面积为{1}", r, pi * r * r);
return pi * r * r;
}
public static float CirclePerimeter(float r){
Console.WriteLine("半径为{0}的圆的周长为{1}", r, 2 * pi * r);
return 2 * pi * r;
}
public static float RectangleArea(float a, float b){
Console.WriteLine("长为{0}宽为{1}的矩形的面积为{2}", a, b, a * b);
return a * b;
}
public static float RectanglePerimeter(float a, float b){
Console.WriteLine("长为{0}宽为{1}的矩形的周长为{2}", a, b, 2 * (a + b));
return 2 * (a + b);
}
public static float Abs(float n){
float n1 = (n > 0)?n:-n;
Console.WriteLine("{0}绝对值为{1}", n, n1);
return n1;
}
static MathCalc(){
Console.WriteLine("静态构造函数执行");
}
}
class Program
{
static void Main(string[] args)
{
MathCalc.CircleArea(5);
MathCalc.CirclePerimeter(5);
MathCalc.RectangleArea(5, 10);
MathCalc.RectanglePerimeter(5, 10);
MathCalc.Abs(-5);
}
}
namespace 拓展方法;
#region 语法
//访问修饰符 static 返回值类型 函数名(this 拓展类名 参数名,参数数据类型 参数, ...){
//
//}
#endregion
#region 示例
static class Tools{
public static void Print(this string str){
Console.WriteLine("为string拓展方法:"+str);
}
public static void PrintInfo(this string str, string str1, int num){
Console.WriteLine("拓展方法的对象:"+str);
Console.WriteLine("传入的参数:"+str1 + " " + num);
}
public static void PrintInfo(this Test t){
Console.WriteLine("为Test类拓展方法:"+t.i);
}
//如果拓展的方法名和类里面的方法重名,优先使用类的方法
public static void Func(this Test t){
Console.WriteLine("为Test类拓展同名方法:");
}
}
#endregion
#region 为自定义的类型拓展方法
class Test{
public int i = 10;
public void Func(){
Console.WriteLine("Test类自己的Func方法");
}
}
#endregion
class Program
{
static void Main(string[] args)
{
string str = "Hello World";
str.Print(); //调用拓展方法
str.PrintInfo("你好", 123); //调用拓展方法
//为自定义的类型拓展方法
Test t = new Test();
t.PrintInfo(); //调用拓展方法
t.Func(); //重名,优先调用类自己的方法
}
}
注意:
如果拓展的方法名和类里面的方法重名,优先使用类的方法
习题
csharp复制代码
namespace 拓展方法习题;
//1
//平方
static class Test{
public static int Square(this int n){
Console.WriteLine("Square of " + n + " is " + (n*n));
return n*n;
}
public static void Suicide(this Player player){
Console.WriteLine("Player " + player.name + " is suiciding!");
}
}
//2
//玩家
class Player{
public string name;
public int hp;
public int atk;
public int def;
public Player(string name, int hp, int atk, int def){
this.name = name;
this.hp = hp;
this.atk = atk;
this.def = def;
}
public void Attack(Player target){
Console.WriteLine(this.name + " attacks " + target.name + "!");
target.hp -= this.atk - target.def;
Console.WriteLine(target.name + " now has " + target.hp + " HP.");
if(this.atk - target.def > 0){
Hurted(target);
}
}
public void Move(int x, int y){
Console.WriteLine(this.name + " moves to (" + x + ", " + y + ").");
}
public void Hurted(Player target){
Console.WriteLine(target.name + " is hurt!");
}
}
class Program
{
static void Main(string[] args)
{
//1
int num = 3;
num.Square();
//2
Player player1 = new Player("player1", 100, 10, 5);
Player player2 = new Player("player2", 100, 13, 2);
player1.Attack(player2);
player1.Move(1, 2);
player1.Suicide();
player2.Attack(player1);
player2.Suicide();
}
}
namespace 运算符重载习题;
//1
class Position{
public int x;
public int y;
public static bool operator ==(Position p1, Position p2){
if(p1.x == p2.x && p1.y == p2.y){
return true;
}
return false;
}
public static bool operator !=(Position p1, Position p2){
if(p1.x!= p2.x || p1.y!= p2.y){
return true;
}
return false;
}
}
//2
class Vector3{
public int x;
public int y;
public int z;
public static Vector3 operator +(Vector3 v1, Vector3 v2){
Vector3 result = new Vector3();
result.x = v1.x + v2.x;
result.y = v1.y + v2.y;
result.z = v1.z + v2.z;
return result;
}
public static Vector3 operator -(Vector3 v1, Vector3 v2){
Vector3 result = new Vector3();
result.x = v1.x - v2.x;
result.y = v1.y - v2.y;
result.z = v1.z - v2.z;
return result;
}
public static Vector3 operator *(Vector3 v1, int n){
Vector3 result = new Vector3();
result.x = v1.x * n;
result.y = v1.y * n;
result.z = v1.z * n;
return result;
}
}
class Program
{
static void Main(string[] args)
{
//1
Position a = new Position();
a.x = 1;
a.y = 2;
Position b = new Position();
b.x = 1;
b.y = 2;
Console.WriteLine(a == b); // True
Console.WriteLine(a!= b); // False
//2
Vector3 v1 = new Vector3();
v1.x = 1;
v1.y = 2;
v1.z = 3;
Vector3 v2 = new Vector3();
v2.x = 2;
v2.y = 3;
v2.z = 4;
Vector3 v3 = v1 + v2;
Console.WriteLine("(v3.x, v3.y, v3.z) = ({0}, {1}, {2})", v3.x, v3.y, v3.z);
Vector3 v4 = v1 - v2;
Console.WriteLine("(v4.x, v4.y, v4.z) = ({0}, {1}, {2})", v4.x, v4.y, v4.z);
Vector3 v5 = v1 * 2;
Console.WriteLine("(v5.x, v5.y, v5.z) = ({0}, {1}, {2})", v5.x, v5.y, v5.z);
}
}
内部类和分部类
内部类------在一个类中申明一个类
要用包裹者点出这个内部类
作用:亲密关系的体现,有点像继承
注意:访问修饰符作用很大
csharp复制代码
namespace 内部类和分部类;
#region 内部类
class Person{
public string name;
public int age;
public Body body;
public class Body{
Arm leftArm;
Arm rightArm;
class Arm{
}
}
}
#endregion
class Program
{
static void Main(string[] args)
{
Person person = new Person();
person.body = new Person.Body();
//访问修饰符的作用,不写public,则无法访问
// person.body.leftArm = new Person.Body.Arm();
}
}
分部类------一个类分成几部分申明
关键字:partial
作用:分部描述一个类,增加程序的可拓展性
注意:
分部类可以写在多个脚本文件中
分部类的访问修饰符要一致
分部类中不能有重复的成员
csharp复制代码
namespace 内部类和分部类;
#region 内部类
class Person{
public string name;
public int age;
public Body body;
public class Body{
Arm leftArm;
Arm rightArm;
class Arm{
}
}
}
#endregion
#region 分部类
partial class Student{
public bool sex;
public string name;
}
partial class Student{
public int age;
//注意不要重复成员名
// public string name;
public void SayHello(){
Console.WriteLine("Hello,I'm {0},age is {1}",name,age);
}
}
#endregion
class Program
{
static void Main(string[] args)
{
//内部类
Person person = new Person();
person.body = new Person.Body();
//访问修饰符的作用,不写public,则无法访问
// person.body.leftArm = new Person.Body.Arm();
//分部类
Student student = new Student();
student.age = 18;
student.name = "Tom";
student.sex = true;
student.SayHello();
}
}
分部方法------将方法的申明和实现分离
注意:
不能加访问修饰符,默认私有
只能在分部类里申明
返回值只能是void
参数不能用out关键字
csharp复制代码
namespace 内部类和分部类;
#region 内部类
class Person{
public string name;
public int age;
public Body body;
public class Body{
Arm leftArm;
Arm rightArm;
class Arm{
}
}
}
#endregion
#region 分部类
partial class Student{
public bool sex;
public string name;
public partial void SayHello();
}
partial class Student{
public int age;
//注意不要重复成员名
// public string name;
public partial void SayHello(){
Console.WriteLine("I'm {0},age:{1}", name, age);
}
}
#endregion
class Program
{
static void Main(string[] args)
{
//内部类
Person person = new Person();
person.body = new Person.Body();
//访问修饰符的作用,不写public,则无法访问
// person.body.leftArm = new Person.Body.Arm();
//分部类
Student student = new Student();
student.age = 18;
student.name = "Tom";
student.sex = true;
student.SayHello();
}
}
继承
继承的基本概念
子类继承父类的所有成员、特征和行为
子类可以有自己的特征和行为
单根性:子类只能有一个父类
传递性 :子类可以间接继承父类的父类
语法
csharp复制代码
namespace 继承;
#region 继承语法
class Teacher{
public string name;
public int number;
public void PrintName(){
Console.WriteLine("My name is " + name + " " + number);
}
}
//子类
class TeachingTeacher : Teacher{
//子类独有的属性
public string subject;
public void PrintSubject(){
Console.WriteLine("My subject is " + subject);
}
}
//子类的子类
class ChineseTeacher : TeachingTeacher{
public void PrintChinese(){
Console.WriteLine("xxxx");
}
}
#endregion
class Program
{
static void Main(string[] args)
{
TeachingTeacher teacher1 = new TeachingTeacher();
teacher1.name = "John";
teacher1.number = 9527;
teacher1.subject = "Math";
teacher1.PrintName();
teacher1.PrintSubject();
//传递性继承
ChineseTeacher teacher2 = new ChineseTeacher();
teacher2.name = "Mary";
teacher2.number = 9528;
teacher2.subject = "Chinese";
teacher2.PrintName();
teacher2.PrintSubject();
teacher2.PrintChinese();
}
}
class Teacher{
public string name;
private int number;
public void PrintName(int number){
this.number = number;
Console.WriteLine("My name is " + name + " " + number);
}
}
//子类
class TeachingTeacher : Teacher{
public new string name;
//子类独有的属性
public string subject;
public void PrintSubject(int number){
//子类里可以调用父类的公共方法来间接传入private参数
PrintName(number);
Console.WriteLine("My subject is " + subject);
}
}
protected:保护的是内部和子类
也就是希望外部不能调用,但是其内部和子类可以调用
csharp复制代码
namespace 继承;
#region 继承语法
class Teacher{
public string name;
protected int number;
public void PrintName(int number){
//内部可以调用protected属性
this.number = number;
Console.WriteLine("My name is " + name + " " + number);
}
}
//子类
class TeachingTeacher : Teacher{
//子类独有的属性
public string subject;
public void PrintSubject(int number){
//子类里可以调用protected属性
this.number = number;
Console.WriteLine("My subject is " + subject);
}
}
//子类的子类
class ChineseTeacher : TeachingTeacher{
public void PrintChinese(int number){
this.number = number;
Console.WriteLine("xxxx");
}
}
#endregion
class Program
{
static void Main(string[] args)
{
//语法
TeachingTeacher teacher1 = new TeachingTeacher();
teacher1.name = "John";
//外部无法调用protected属性number
// teacher1.number = 9527;
teacher1.subject = "Math";
//只能公共方法来传参,因为内部和子类可以访问protected属性
teacher1.PrintName(9527);
teacher1.PrintSubject(9527);
//传递性继承
ChineseTeacher teacher2 = new ChineseTeacher();
teacher2.name = "Mary";
//外部无法调用protected属性number
// teacher2.number = 9528;
teacher2.subject = "Chinese";
//只能公共方法来传参,因为内部和子类可以访问protected属性
teacher2.PrintName(9528);
teacher2.PrintSubject(9528);
teacher2.PrintChinese(9528);
}
}
internal:内部的,只有在同一个程序集文件中,内部类型或成员才能访问
在后面命名空间再讲internal
子类和父类的同名成员
C#中允许出现子类和父类存在同名成员:
子类里可以命名和父类同名的成员,但是声明子类对象的时候,子类的成员会顶替父类。
最好别用这个特性
要用的时候,最好在前面加上new,用来表明这是一个新的成员,顶替父类里的同名成员。
习题
csharp复制代码
namespace 继承习题;
class Person{
public string name;
public int age;
public void SayHello(){
Console.WriteLine("Hello, my name is " + name + " and I am " + age + " years old.");
}
}
class Warrior : Person{
public void Atk(Person person){
Console.WriteLine("Warrior {0} attacks {1}!", name, person.name);
}
}
class Program
{
static void Main(string[] args)
{
Person person = new Person();
person.name = "Alice";
Warrior warrior = new Warrior();
warrior.name = "John";
warrior.age = 30;
warrior.SayHello();
warrior.Atk(person);
}
}
继承_里氏替换原则
面向对象七大原则
开闭原则:对扩展开放,对修改封闭
单一职责原则:一个类只负责一项职责
依赖倒置原则:高层模块不应该依赖低层模块,二者都应该依赖抽象
接口隔离原则:使用多个专门的接口,而不使用单一的总接口
迪米特法则:一个对象应该对其他对象有最少的了解
里氏替换原则:子类对象必须能够替换其父类对象
违反开闭原则:违反开闭原则的设计是不好的设计
里氏替换原则的基本概念
任何父类出现的地方,子类都可以替代
父类容器装子类对象,因为子类对象包含了父类的所有内容
作用:方便进行对象储存和管理
语法
csharp复制代码
namespace 继承_里氏替换原则;
class GameObject{
}
class Player : GameObject{
public void Attack(GameObject target){
Console.WriteLine("{0}Attack{1}", this, target);
}
}
class Monster : GameObject{
}
class Boss : GameObject{
}
class Program
{
static void Main(string[] args)
{
#region 里氏替换原则语法
//父类容器 装 子类对象
GameObject player = new Player();
GameObject monster = new Monster();
GameObject boss = new Boss();
GameObject[] objects = new GameObject[] { new Player(), new Monster(), new Boss()};
#endregion
}
}
这样写了之后不能子类名.调用其方法,所以有了下面的两个关键字。
is 和 as 关键字
is判断,as转换
is:判断一个对象是否是指定类对象
返回:bool
语法很接近自然语言
csharp复制代码
#region is 和 as 关键字
if(player is Player){
}
else if(player is Monster){
}
#endregion
as:讲一个对象转换为指定类对象
返回:指定类型对象,否则失败就返回null
csharp复制代码
Player p = player as Player;
Monster m = player as Monster;
Console.WriteLine(p);
Console.WriteLine(m == null);//true
is和as配合使用
csharp复制代码
#region is和as的配合使用
if(player is Player){
// Player p1 = player as Player;
// p1.Attack(monster);
(player as Player).Attack(monster);
}
#endregion
csharp复制代码
//当不知道objects数组里的内容,需要遍历判断
for(int i = 0; i < objects.Length; i++){
//objects数组里的Player类型对象
if(objects[i] is Player){
(objects[i] as Player).Attack(monster);
}
else if(objects[i] is Monster){
//objects数组里的Monster类型对象
//...
}
else if(objects[i] is Boss){
//objects数组里的Boss类型对象
//...
}
}
不能用子类容器装父类对象
因为父类对象的方法比子类少
csharp复制代码
// Player p2 = new GameObjects(); //错误
习题
csharp复制代码
namespace 继承_里氏替换原则习题;
class Monster{
}
class Boss : Monster{
public void Skill(){
Console.WriteLine("Boss Skill");
}
}
class Goblin : Monster{
public void Atk(){
Console.WriteLine("Goblin Atk");
}
}
class Player{
public Weapon nowWeapon;
public Player(){
//默认武器:匕首
nowWeapon = new Dagger();
}
//切换武器
public void Converse(Weapon weapon){
nowWeapon = weapon;
}
public void PrintWeapon(){
Console.WriteLine("now I have a {0}", nowWeapon.name);
}
}
class Weapon{
public string name;
}
class MP9 : Weapon{
public MP9(){
name = "MP9";
}
}
class ShotGun : Weapon{
public ShotGun(){
name = "ShotGun";
}
}
class Pistol : Weapon{
public Pistol(){
name = "Pistol";
}
}
class Dagger : Weapon{
public Dagger(){
name = "Dagger";
}
}
class Program
{
static void Main(string[] args)
{
//习题1
Random r = new Random();
int randomNum;
Monster[] monsters = new Monster[10];
//随机生成10个怪物,二者生成概率都是50%
for(int i = 0; i < monsters.Length; i++){
randomNum = r.Next(0,2);
if(randomNum == 1){
monsters[i] = new Boss();
}
else{
monsters[i] = new Goblin();
}
}
for(int i = 0; i < monsters.Length; i++){
if(monsters[i] is Boss){
(monsters[i] as Boss).Skill();
}
else if(monsters[i] is Goblin){
(monsters[i] as Goblin).Atk();
}
}
//习题2
Player player = new Player();
player.PrintWeapon();
Console.WriteLine("下面开始捡东西");
Weapon[] weapons = new Weapon[4]{new MP9(), new ShotGun(), new Pistol(), new Dagger()};
for(int i = 0; i < weapons.Length; i++){
player.Converse(weapons[i]);
player.PrintWeapon();
}
}
}
继承中的构造函数
当申明一个子类对象的时候,先执行父类的构造函数,再执行子类的构造函数
注意:
父类的无参构造
子类默认调用的是父类的无参构造函数
csharp复制代码
#region 父类无参构造函数的重要性
class Parent{
//有参构造函数会顶掉无参构造函数
public Parent(int i){
Console.WriteLine("Parent 构造函数");
}
}
class Child: Parent{
//子类默认调用的是父类的无参构造函数
//找不到父类无参构造函数,会报错
public Child(){
Console.WriteLine("Child 构造函数");
}
}
#endregion
子类可以通过base关键字,调用父类的指定构造函数
(先调用父类,再调用子类另外写的)
csharp复制代码
#region 通过base关键字,调用父类的指定构造函数
class Parent{
//有参构造函数会顶掉无参构造函数
public Parent(int i){
Console.WriteLine("Parent 有参int构造函数");
}
public Parent(string str){
Console.WriteLine("Parent 有参string构造函数");
}
}
class Child: Parent{
//base(传入父类构造函数的参数)
public Child(int i) : base(i){
Console.WriteLine("Child int参数构造函数");
}
public Child(string str) : base(str){
Console.WriteLine("Child str参数构造函数");
}
//调用父类的有参int构造函数
public Child(int i, string str) : base(i){
Console.WriteLine("Child 两个参数构造函数");
}
//通过调用该子类的第一个构造函数(int i),
// 间接调用父类的有参int构造函数
// public Child(int i, string str):this(i){
// }
// 和上面是等价的
}
#endregion
习题
csharp复制代码
namespace 继承_构造函数习题;
class Worker{
public string jobType;
public string jobContent;
public Worker(){
}
public Worker(string jobType, string jobContent){
this.jobType = jobType;
this.jobContent = jobContent;
}
public void Work(){
Console.WriteLine("I am a worker, my job is {0} and my job content is {1}", jobType, jobContent);
}
}
class Programmer : Worker{
public Programmer():base("Programmer", "Coding"){
}
}
class Strategist : Worker{
public Strategist():base("Strategist", "Planning"){
}
}
class Artist : Worker{
public Artist():base("Artist", "Drawing"){
}
}
class Program
{
static void Main(string[] args)
{
Programmer p = new Programmer();
p.Work();
Strategist s = new Strategist();
s.Work();
Artist a = new Artist();
a.Work();
}
}
万物之父和装箱拆箱
万物之父
关键字:object
object是所有类型的基类
作用:
利用里氏替换原则,用object作为父类容器装所有对象
用来表示不确定类型,作为函数的参数类型
csharp复制代码
namespace 继承_万物之父与装箱拆箱;
class Father{
public void Speak(){
Console.WriteLine("I am Father.");
}
}
class Son : Father{
}
class Program
{
static void Main(string[] args)
{
//用object容器装任意对象
object o = new Son();
Son s = new Son();
o = s;
Father f = new Father();
o = f;
//用is和as来调用容器里的对象的成员
if(o is Father)(o as Father).Speak();
#region object对象的类型转换
//值类型 ------ 强转
//用object容器装任意值类型
object o2 = 1f;
//要当成数字用的话,需要强转
int f1 = (int)o2;
//引用类型 ------ as转换
//string类型
object o3 = "111";
string s1 = (string)o3;
s1 = o3 as string;
s1 = o3.ToString();
//数组类型
object o4 = new int[3];
int[] arr = o4 as int[];
arr = (int[])o4;
#endregion
}
}
class Father{
}
//表示这就是最后一个子类,不能再往下继承了
//类似结扎
sealed class Son : Father{
}
作用:
在面向对象程序设计中,密封类就是不允许某个最底层子类再被继承
可以保证程序的规范性和安全性
继承部分综合习题
csharp复制代码
namespace 继承_综合习题;
class Car{
public int speed;
public int maxSpeed;
//当前的人数
public int num;
public Person[] persons;
public Car(int speed, int maxSpeed, int maxNum){
this.speed = speed;
this.maxSpeed = maxSpeed;
this.num = 0;
persons = new Person[maxNum];
}
public void AddPerson(Person person){
if(num >= persons.Length){
Console.WriteLine("车子已满");
return;
}
persons[num] = person;
num++;
}
public void RemovePerson(Person delPerson){
if(delPerson is Driver){
Console.WriteLine("驾驶员不能下车");
return;
}
//只有乘客下车
else{
for(int i = 0; i < persons.Length; i++){
//结束循环的条件是找到空位
if(persons[i] == null){
break;
}
// 找到要删除的对象
else if(persons[i] == delPerson){
// 找到了要删除的对象,将其后面的元素前移一位
for(int j = 0; j < num - 1; j++){
persons[j] = persons[j+1];
}
//最后一个位置清空
persons[num-1] = null;
num--;
break;
}
}
}
}
public void Move(){
}
public void Accident(){
if(speed > maxSpeed)
Console.WriteLine("发生事故");
else
Console.WriteLine("正常");
}
public void PrintNum(){
Console.WriteLine("当前车子乘客数量:" + num);
}
}
class Person{
}
class Driver:Person{
}
class Passenger:Person{
}
class Program
{
static void Main(string[] args)
{
Person d1 = new Driver();
Person p1 = new Passenger();
Person p2 = new Passenger();
Car car = new Car(60, 80, 5);
car.AddPerson(d1);
car.AddPerson(p1);
car.AddPerson(p2);
car.PrintNum();
car.RemovePerson(d1);
car.PrintNum();
car.RemovePerson(p1);
car.PrintNum();
car.Accident();
car.speed = 100;
car.Accident();
}
}
多态
多态的基本概念
让继承同一父类的子类们,在执行相同方法的时候有不同的表现
解决问题:让同一个对象有唯一行为的特征
csharp复制代码
class Father
{
public void SpeakName()
{
Console.WriteLine("Father类");
}
}
class Son : Father
{
public new void SpeakName()
{
Console.WriteLine("Son类");
}
}
class Program
{
static void Main(string[] args)
{
//如果用里氏替换原则
Father s = new Son();
s.SpeakName(); //这里会打印父类的方法
//只有用as转换成子类对象才会调用子类的方法
(s as Son).SpeakName();
}
}
namespace 多态_vob;
class GameObject
{
public string name;
public GameObject(string name)
{
this.name = name;
}
public virtual void Atk()
{
Console.WriteLine("GameObject对象的攻击");
}
}
class Player : GameObject
{
//子类默认找的是父类的无参构造函数,但是上面父类中已经有参构造,顶掉了无参构造
//所以需要:base()继承构造函数
public Player(string name) : base(name)
{
}
//虚函数可以被子类重写
public override void Atk()
{
//base.Atk(); //保留父类的虚函数行为,可以写在这个override方法的任何需要的地方
Console.WriteLine("Player对象的攻击");
}
}
class Program
{
static void Main(string[] args)
{
GameObject p1 = new Player("sb");
p1.Atk();
//这就和原来用as的方式结果一样,但是可读性更强
// (p1 as Player).Atk();
}
}
什么时候需要base?
需要保留父类行为就base.方法名()
习题
csharp复制代码
using System.Drawing;
namespace 多态_vob习题;
//1
class Duck
{
public virtual void Speak()
{
Console.WriteLine("嘎嘎叫");
}
}
class woodenDuck : Duck
{
public override void Speak()
{
Console.WriteLine("吱吱叫");
}
}
class rubberDuck : Duck
{
public override void Speak()
{
Console.WriteLine("唧唧叫");
}
}
//3
class Graph
{
public virtual float area()
{
return 0;
}
public virtual float perimeter()
{
return 0;
}
}
class Rectangle : Graph
{
public float width, length;
public Rectangle(float width, float length)
{
this.width = width;
this.length = length;
}
public override float area()
{
return width * length;
}
public override float perimeter()
{
return 2 * (width + length);
}
}
class Square : Graph
{
public float sideLength;
public Square(float sideLength)
{
this.sideLength = sideLength;
}
public override float area()
{
return sideLength * sideLength;
}
public override float perimeter()
{
return sideLength * 4;
}
}
class Sphere : Graph
{
public float radius;
public Sphere(float radius)
{
this.radius = radius;
}
public override float area()
{
return radius * 3.14f * 3.14f;
}
public override float perimeter()
{
return radius * 2 * 3.14f;
}
}
class Program
{
static void Main(string[] args)
{
//1
Duck d1 = new Duck();
d1.Speak();
Duck w1 = new woodenDuck();
w1.Speak();
Duck r1 = new rubberDuck();
r1.Speak();
//3
Graph rect1 = new Rectangle(1, 2);
Console.WriteLine(rect1.area());
Console.WriteLine(rect1.perimeter());
Graph square1 = new Square(1);
Console.WriteLine(square1.area());
Console.WriteLine(square1.perimeter());
Graph sphere1 = new Sphere(1);
Console.WriteLine(sphere1.area());
Console.WriteLine(sphere1.perimeter());
}
}
抽象类和抽象函数
抽象类
关键字:abstract
特点:
不能被实例化,但是可以用里氏替换原则作为容器存储对象
可以包含抽象方法
继承抽象类必须重写他的抽象方法
csharp复制代码
namespace 多态_抽象类和抽象函数;
abstract class Thing
{
public string name;
//抽象函数
}
class Water : Thing
{
}
class Program
{
static void Main(string[] args)
{
//抽象类不能被实例化
// Thing t = new Thing(); // 错误
//抽象类的子类可以用里氏替换原则用父类装子类
Thing t = new Water();
//抽象类的子类可以被实例化
Water w = new Water();
}
}
抽象函数
又名:纯虚方法
关键字:abstarct
特点:
只能在抽象类中申明
没有函数体,就是不要写花括号{}
不能是私有的
继承后必须用override重写
csharp复制代码
abstract class Thing
{
public string name;
//抽象函数
public abstract void Show();
//虚函数
public virtual void Test()
{
}
}
class Water : Thing
{
//继承一个有抽象函数的抽象类,必须要实现抽象函数
public override void Show()
{
}
//虚函数可以选择是否要覆盖
public override void Test()
{
}
}
//接口的使用
class Animal
{
}
class Person : Animal, IFly
{
//实现接口中的函数,可以申明为虚函数virtual,在子类中重写
public virtual void Fly()
{
}
public string Name { get; set; }
public int this[int index]
{
get
{
return 0;
}
set
{
}
}
public event Action doSomething;
}
接口遵循里氏替换原则
csharp复制代码
class Program
{
static void Main(string[] args)
{
//接口不能实例化
// IFly f = new IFly(); //错误
//接口可以作为容器,里氏替换原则
IFly f = new Person();
}
}
接口可以继承接口
接口继承接口后,不需要实现
类继承接口后,类必须实现所有内容
csharp复制代码
//接口继承接口
interface IWalk
{
void Walk();
}
//接口继承接口,不需要实现
interface IMove : IWalk, IFly
{
}
//类继承接口,必须实现所有成员
class Test : IMove
{
public string Name { get => throw new NotImplementedException(); set => throw new NotImplementedException(); }
public int this[int index] { get => throw new NotImplementedException(); set => throw new NotImplementedException(); }
public event Action doSomething;
public void Fly()
{
}
public void Walk()
{
}
}
里氏替换,接口作为容器
csharp复制代码
IMove im = new Test();
IFly ifly = new Test();
IWalk iw = new Test();
//用什么接口装,其对象就只能是该接口含有的方法
im.Walk();
im.Fly();
//IFly只有Fly()
ifly.Fly();
//IWalk只有Walk()
iw.Walk();
//显示实现的使用
IAtk ia = new Player();
ISuperAtk isa = new Player();
ia.Atk();
isa.Atk();
Player p = new Player();
(p as IAtk).Atk();
(p as ISuperAtk).Atk();
p.Atk();
总结
继承类:是对象间的继承
继承接口:行为间的继承,继承接口里的行为规范去实现内容
接口可以作为容器装对象
接口的引入,可以实现装载各种不同类但是有相同行为的对象
特别注意:
接口包含 成员方法、属性、索引器、事件,并且都不实现,都没有访问修饰符
可以继承多个接口,但只能继承一个类
接口可以继承接口,这就相当于行为的合并,在子类继承的时候再去实现具体的行为
接口可以被显示实现,用来解决不同接口中的同名函数的不同实现
接口方法在子类实现的时候可以加virtual申明为虚函数,然后在之后的子类中重写
习题
csharp复制代码
namespace 多态_接口习题;
//1.
interface IRegister
{
void Register();
}
class Person : IRegister
{
public void Register()
{
Console.WriteLine("在派出所登记");
}
}
class Car : IRegister
{
public void Register()
{
Console.WriteLine("在车管所登记");
}
}
class House : IRegister
{
public void Register()
{
Console.WriteLine("在房管局登记");
}
}
//2.
abstract class Animal
{
public abstract void Walk();
}
interface IFly
{
void Fly();
}
interface ISwim
{
void Swim();
}
class helicopter : IFly
{
public void Fly()
{
Console.WriteLine("直升机开始飞");
}
}
class sparrow : Animal, IFly
{
public override void Walk()
{
}
public void Fly()
{
}
}
class Ostrich : Animal
{
public override void Walk()
{
}
}
class Penguin : Animal, ISwim
{
public void Swim()
{
}
public override void Walk()
{
}
}
class Parrot : Animal, IFly
{
public void Fly()
{
}
public override void Walk()
{
}
}
class Swan : Animal, ISwim,IFly
{
public void Swim()
{
}
public void Fly()
{
}
public override void Walk()
{
}
}
//3.
interface IUSB
{
void ReadData();
}
class Computer
{
public IUSB usb1;
}
class StorageDevice : IUSB
{
public string name;
public StorageDevice(string name)
{
this.name = name;
}
public void ReadData()
{
Console.WriteLine(name + "读取数据");
}
}
class Mp3 : IUSB
{
public void ReadData()
{
Console.WriteLine("mp3读取数据");
}
}
class Program
{
static void Main(string[] args)
{
//1.
IRegister[] arr = new IRegister[] { new Person(), new Car(), new House() };
foreach (IRegister item in arr)
{
item.Register();
}
//3.
Computer cp1 = new Computer();
cp1.usb1 = new Mp3();
cp1.usb1.ReadData();
cp1.usb1 = new StorageDevice("硬盘");
cp1.usb1.ReadData();
}
}
//Equals()
//比较的是二者指向的内存地址是否一样
//最终判断权交给左侧对象的Equals方法
//值类型
Console.WriteLine(Object.Equals(1, 1));
Console.WriteLine(1.Equals(1));
//引用类型
Test t1 = new Test();
Test t2 = new Test();
Console.WriteLine(Object.Equals(t1, t2));
Console.WriteLine(t1.Equals(t2));
t2 = t1;
Console.WriteLine(Object.Equals(t1, t2));
Console.WriteLine(t1.Equals(t2));
ReferenceEquals()
作用:比较两个对象是否是相同的引用(内存地址)
csharp复制代码
//ReferenceEquals()
//比较两个对象是否是相同的引用(内存地址)
//值类型:返回值永远是flase
Console.WriteLine(Object.ReferenceEquals(1, 1));
//引用类型:
Test t3 = new Test();
Test t4 = new Test();
Console.WriteLine(Object.ReferenceEquals(t3, t4));
t4 = t3;
Console.WriteLine(Object.ReferenceEquals(t3, t4));
object可以省略,因为是万物之父,只要在类里,这个类肯定继承于Object,所以也包含这个方法
object中的成员方法
普通方法GetType()
作用:获取对象运行时的类型Type,返回一个Type类型的对象
通过Type结合后面的反射相关特性,可以做很多关于对象的操作
csharp复制代码
//普通方法Type()
Test t5 = new Test();
Type type = t5.GetType();
普通方法MemberwiseClone()
作用:获取对象的浅拷贝对象,返回一个新的对象。
但是新对象(克隆体)的引用变量改了之后,老对象相应的引用变量也会改变
csharp复制代码
class Test
{
//值类型成员变量
public int i = 1;
//引用类型成员变量
public Test2 ttt = new Test2();
public Test Clone()
{
return MemberwiseClone() as Test;
}
}
class Test2
{
public int i = 2;
}
namespace 万物之父中的方法习题;
//1.
class Player
{
private string name;
private int hp;
private int atk;
private int def;
private int miss;
public Player(string name, int hp, int atk, int def, int miss)
{
this.name = name;
this.hp = hp;
this.atk = atk;
this.def = def;
this.miss = miss;
}
public override string ToString()
{
return String.Format("姓名{0},血量{1},攻击{2},防御{3},闪避{4}", name, hp, atk, def, miss);
}
}
//2.
class Monster
{
public Monster m;
public string Name { get; set; }
public int Hp{ get; set; }
public int Atk{ get; set; }
public int Def{ get; set; }
public int SkillID{ get; set; }
public Monster(string name, int hp, int atk, int def, int skillID)
{
Name = name;
Hp = hp;
Atk = atk;
Def = def;
SkillID = skillID;
m = this;
}
public Monster Clone()
{
return MemberwiseClone() as Monster;
}
}
class Program
{
static void Main(string[] args)
{
//1.
Player p = new Player("张三", 100, 10, 5, 5);
Console.WriteLine(p);
//2.
Monster A = new Monster("A", 100, 10, 5, 5);
Monster B = A.Clone();
B.Name = "B";
Console.WriteLine(A.Name);
//因为是值类型的,所以改克隆体不会改变原来的值
B.m.Name = "B";
Console.WriteLine(A.m.Name);
//引用类型的内容改变,改克隆体,原来的值也会改变
}
}
//正向查找字符位置
//找不到返回-1
int index = str.IndexOf("o");
Console.WriteLine(index);
//忽略大小写,StringComparison.OrdinalIgnoreCase
index = str.IndexOf("o",StringComparison.OrdinalIgnoreCase);
Console.WriteLine(index);
//装箱
stack.Push(1); // int 值类型 → object 引用类型(装箱)
stack.Push(true); // bool 值类型 → object 引用类型(装箱)
//拆箱
object o = stack.Pop();
int num = (int)o; // 拆箱:object → int
如何避免装箱拆箱?
泛型集合:栈泛型 Stack <Type>
相当于一个只有栈的存储特性(后进先出)的集合,缺点就是只能存指定类型的元素
入栈出栈都是直接存取,不存在装箱拆箱
csharp复制代码
Stack<int> stack = new Stack<int>();
stack.Push(100);
int value = stack.Pop(); // 直接获取 int,无需拆箱
栈的应用
UI的显示逻辑(每次点击的面板总是显示在最前面)
高进制转低进制
习题
对于取出一个后进先出的数组,可以用栈来解决
csharp复制代码
using System.Collections;
Console.WriteLine("请输入一个整数");
uint num = (uint.Parse)(Console.ReadLine());
DecToBinary(num);
static void DecToBinary(uint num)
{
Stack stack = new Stack();
while (num != 0)
{
stack.Push(num % 2);
num /= 2;
}
Console.Write("二进制为:");
while (stack.Count != 0)
{
Console.Write(stack.Pop());
}
Console.WriteLine();
}
Queue
本质是object[]数组,先进先出,类似管道
队列的使用
csharp复制代码
using System.Collections;
Queue queue = new Queue();
//增删查改
//入队
queue.Enqueue(1);
queue.Enqueue(2);
queue.Enqueue(3);
queue.Enqueue(new Test());
//出队
object o = queue.Dequeue();
Console.WriteLine(o);
//查看队头
o = queue.Peek();
Console.WriteLine(o);
//是否在队列中
if (queue.Contains(3)) Console.WriteLine("在队列中");
//清空
// queue.Clear();
//遍历
//1.长度
Console.WriteLine(queue.Count);
//2.用foreach遍历
foreach (object item in queue)
{
Console.WriteLine(item);
}
//3.转成object[]数组遍历
object[] arr = queue.ToArray();
for (int i = 0; i < arr.Length; i++)
{
Console.WriteLine(arr[i]);
}
//4.循环出队列
while (queue.Count > 0)
{
Console.WriteLine(queue.Dequeue());
}
class Test
{
}
队列的装箱拆箱
csharp复制代码
//装箱
queue.Enqueue(123);
//拆箱
int num = (int)queue.Dequeue();
和栈一样,用泛型 Queue<Type> 就可以避免装箱拆箱
习题
csharp复制代码
using System.Collections;
Queue queue = new Queue();
for (int i = 0; i < 10; i++)
{
queue.Enqueue(i);
}
while (queue.Count > 0)
{
Console.WriteLine(queue.Dequeue());
//隔停100毫秒
Task.Delay(100).Wait();
}
Hashtable
哈希表/散列表,本质是一个字典
是基于键的哈希代码组织起来的键值对 <key,value>
用键来访问集合中的元素
哈希表的使用
csharp复制代码
using System.Collections;
Hashtable hashtable = new Hashtable();
//增删查改
//增
//可以有相同value,但是不能有相同key
hashtable.Add(1, 123);
hashtable.Add("123", 321);
hashtable.Add(true, false);
//或者直接用索引器加,用索引器加相同key的时候相当于改了对应value
hashtable[1] = 123;
//删
//1.只能通过key来删
hashtable.Remove(1);
//2.删除不存在的键,不会报错
hashtable.Remove("1");
//3.清空
// hashtable.Clear();
//查
//1.通过key来查,找不到返回空
Console.WriteLine(hashtable[1]);
//2.通过key查是否存在键值对
if (hashtable.Contains("123")) Console.WriteLine("存在");
if (hashtable.ContainsKey("123")) Console.WriteLine("存在");
//3.通过value查是否存在键值对
if (hashtable.ContainsValue(321)) Console.WriteLine("存在");
//改
//只能改key对应的value,不能改key
hashtable[1] = 321;
Console.WriteLine(hashtable[1]);
//遍历
//键值对数
Console.WriteLine(hashtable.Count);
//通过key遍历:可以遍历key和value
foreach (var key in hashtable.Keys)
{
Console.WriteLine("key:{0},value:{1}",key ,hashtable[key]);
}
//通过value遍历:只能遍历value
foreach (var value in hashtable.Values)
{
Console.WriteLine("value:{0}",value);
}
//迭代器遍历键值对
foreach (var item in hashtable)
{
Console.WriteLine(item);
}
//迭代器遍历
IDictionaryEnumerator enumerator = hashtable.GetEnumerator();
bool flag = enumerator.MoveNext();
while (flag)
{
Console.WriteLine("key:{0},value:{1}",enumerator.Key,enumerator.Value);
flag = enumerator.MoveNext();
}
Hashtable table = new Hashtable();
// 装箱:int → object
table.Add(1, 100); // key 和 value 都是值类型,会装箱
// 拆箱:object → int
int key = 1;
int value = (int)table[key]; // 拆箱操作
用字典泛型Dictionary <Type,Type>来避免装箱拆箱:
csharp复制代码
Dictionary<int, int> dict = new Dictionary<int, int>();
dict.Add(1, 100);//直接加
int value = dict[1]; // 直接取用
习题
csharp复制代码
using System.Collections;
for (int i = 0; i < 10; i++)
{
MonsterManager.Instance.AddMonster();
}
MonsterManager.Instance.RemoveMonster(1);
MonsterManager.Instance.RemoveMonster(5);
class MonsterManager
{
//要让管理器是唯一的 所以用单例模式来实现
private static MonsterManager _instance = new MonsterManager();
public static MonsterManager Instance
{
get
{
return _instance;
}
}
private Hashtable monsterTable = new Hashtable();
//不让在外面new
private MonsterManager()
{
}
private int monsterID = 0;
public void AddMonster()
{
Monster monster = new Monster(monsterID);
monsterTable.Add(monster.id, monster);
(monsterTable[monsterID] as Monster).Generate();
monsterID++;
}
public void RemoveMonster(int id)
{
if (monsterTable.ContainsKey(id))
{
(monsterTable[id] as Monster).Dead();
monsterTable.Remove(id);
}
}
}
class Monster
{
public int id;
public Monster(int id)
{
this.id = id;
}
public void Generate()
{
Console.WriteLine("生成怪物{0}", id);
}
public void Dead()
{
Console.WriteLine("怪物{0}死亡", id);
}
}
class TestClass<T>
{
public T value;
}
//重载------多个泛型占位字母
class TestClass<T1,T2>
{
public T1 value;
public T2 value2;
}
class Program
{
static void Main(string[] args)
{
//类型占位符T可以用任意数据类型代替,这样就实现了类型的参数化
TestClass<int> t = new TestClass<int>();
t.value = 10;
TestClass<string> t2 = new TestClass<string>();
t2.value = "hello world";
TestClass<int, string> t3 = new TestClass<int, string>();
t3.value = 10;
t3.value2 = "111";
}
}
泛型接口
csharp复制代码
#region 泛型接口
interface TestInterface<T>
{
//接口只能有属性、方法、事件、索引器
T value { get; set; }
}
//在类中实现接口,因为是实现,所以必须在<>内注明数据类型
class Test : TestInterface<int>
{
public int value { get; set; }
}
#endregion
泛型方法(函数)
不确定泛型类型的时候可以用default(T)来获取默认值,然后在后面写函数逻辑
csharp复制代码
#region 普通类中的泛型方法
class Test2
{
public void TestFunc<T>(T value)
{
Console.WriteLine(value);
}
//无参
public void TestFunc<T>()
{
T t = default(T);
Console.WriteLine("{0}类型的默认值是{1}", typeof(T), t);
}
//占位符作为返回值类型
public T TestFunc<T>(string v)
{
return default(T);
}
//多个占位符
public void TestFunc<T, T2>(T v1, T2 v2)
{
}
}
#endregion
class Program
{
static void Main(string[] args)
{
//泛型方法
Test2 t4 = new Test2();
t4.TestFunc<int>(10);
t4.TestFunc<string>("hello world");
t4.TestFunc<double>();
Console.WriteLine(t4.TestFunc<int>("1"));
}
}
泛型类中的泛型方法
csharp复制代码
#region 泛型类中的泛型方法
class Test2<T>
{
public T value;
//函数名后没有<>,不是泛型方法
// 调用函数的时候,参数类型T已经被类的T定死,无法重新指定其数据类型
public void TestFunc(T v)
{
}
//函数名后有<>,才是泛型方法
// 括号里的参数类型T只与该函数的<T>一致,和类的T无关
public void TestFunc<T>(T v)
{
}
}
#endregion
class Program
{
static void Main(string[] args)
{
//泛型类中的泛型方法
Test2<int> t5 = new Test2<int>();
t5.TestFunc<int>(10);
t5.TestFunc<string>("hello world");
t5.TestFunc("111"); //编译器会自动推算出T的类型为string,但最好写上,不然可读性不高
}
}
namespace 泛型约束;
#region 各个泛型类型约束
//值类型约束
class Test1<T> where T : struct
{
public T value;
public void TestFunc<K>(K v) where K : struct
{
}
}
//引用类型约束
class Test2<T> where T : class
{
public T value;
public void TestFunc<K>(K v) where K : class
{
}
}
//公共无参构造约束
class Test3<T> where T : new()
{
public T value;
public void TestFunc<K>(K v) where K : new()
{
}
}
class Test1
{
}
class Test2
{
public Test2(int a)
{
}
}
class Test3
{
private Test3()
{
}
}
abstract class Test4
{
}
//类约束:某个类本身或其子类
class Test4<T> where T : Test1
{
public T value;
public void TestFunc<K>(K v) where K : Test1
{
}
}
class Test1_ : Test1
{
}
//接口约束:某个接口或者其子接口或其子类
interface IFly
{
}
interface IMove : IFly
{
}
class Test6 : IFly
{
}
class Test5<T> where T : IFly
{
public T value;
}
//另一个泛型约束
//前者必须是后者本身或其派生类型
class Test7<T, U> where T : U
{
public T value;
public void TestFunc<K, V>(K k) where K : V
{
}
}
#endregion
class Program
{
static void Main(string[] args)
{
//值类型
Test1<int> t = new Test1<int>();
t.TestFunc<bool>(true);
// Test1<object> t2 = new Test1<object>(); 错误
//引用类型
Test2<string> t2 = new Test2<string>();
t2.TestFunc<object>(new object());
//无参公共构造函数
Test3<Test1> t3 = new Test3<Test1>();
// Test3<Test2> t3 = new Test3<Test2>(); 错误,必须要有无参公共构造函数
// Test3<Test3> t3 = new Test3<Test3>(); 错误,必须要有无参公共构造函数
// Test3<Test4> t3 = new Test3<Test4>(); 错误,抽象类不行,因为抽象类不能new对象,只能在子类继承
Test3<int> t4 = new Test3<int>(); //正确,所有的值类型实际上都默认有一个无参构造
//类约束:某个类本身或其子类
Test4<Test1> t5 = new Test4<Test1>();
t5.TestFunc<Test1>(new Test1());
//Test1_是Test1的子类
Test4<Test1_> t6 = new Test4<Test1_>();
//接口约束
//接口本身
Test5<IFly> t7 = new Test5<IFly>();
t7.value = new Test6();
//接口的实现类(子类)
Test5<Test6> t8 = new Test5<Test6>();
//接口的子接口
Test5<IMove> t9 = new Test5<IMove>();
//另一个泛型约束
//同一类型
Test7<int, int> t10 = new Test7<int, int>();
Test7<Test1, Test1> t11 = new Test7<Test1, Test1>();
//前是后的派生类型
Test7<Test1_, Test1> t12 = new Test7<Test1_, Test1>();
Test7<Test6, IFly> t13 = new Test7<Test6, IFly>();
}
}
约束的组合使用
用 逗号连接两个约束,相当于多个约束条件
注意:
但不是每个都能组合起来使用,看报错
new()一般写在最后
csharp复制代码
#region 约束的组合使用
//同时是引用类型且必须有无参构造函数
class Test8<T> where T : class, new()
{
}
class Test8_
{
}
#endregion
csharp复制代码
#region 约束的组合使用
Test8<Test8_> t14 = new Test8<Test8_>();
#endregion
多个泛型有约束
每个泛型字母都要对应一个 where
csharp复制代码
#region 多个泛型有约束
class Test9<T,U> where T : class, new() where U : struct
{
}
#endregion
习题
csharp复制代码
namespace 泛型约束习题;
//1. 泛型实现单例模式
class SingleBase<T> where T : new()
{
private static T _instance = new T();
public static T Instance
{
get
{
return _instance;
}
}
}
class Test : SingleBase<Test>
{
}
//2. 泛型实现一个不确定类型的ArrayList
class ArrayList<T>
{
private T[] array;
public void Add(T value)
{
//...
}
public void RemoveAt(int index)
{
//...
}
public void Remove(T value)
{
//...
}
public T this[int index]
{
get
{
return array[index];
}
set
{
array[index] = value;
}
}
}
常用泛型数据结构类型
List------列表,泛型ArrayList
本质:一个可变类型的泛型数组,也就是泛型实现的ArrayList
类型在申明时就确定好,所以不存在装箱拆箱
List的申明
csharp复制代码
using System.Collections.Generic;
//申明
List<int> list = new List<int>();