C#OOP学习笔记

第一部分 C#练习实例代码

第一题:1-100的数中,判断每个数是奇数还是偶数

static void Main(string[] args)
{
            for (int i = 1; i <=100;i++ )
            {
                if(i%2==0)
                {
                  Console.WriteLine(i+"是偶数");
                }
                else
                {
                    Console.WriteLine(i + "是奇数");
                }
            }
            Console.ReadLine();
  }

第二题输入一个字符串,计算出该字符串有字母、数字和符号的总数,并显示每个字符字母的位置和是哪个字母

static void Main(string[] args)
{
      Console.WriteLine("请输入一个字符串");
      string str_txt = Console.ReadLine();
      int zimu_count=0;
      int shuzi_count=0;
      int fuhao_count = 0;
      int weizhi = 1;
      foreach (char ch in str_txt)
      {
           if (char.IsLetter(ch))
           {
                zimu_count++;
                Console.WriteLine("第"+weizhi+"位是字母"+ch);
            }
            if (char.IsDigit(ch))
            {
                 shuzi_count++;
                 Console.WriteLine("第" + weizhi + "位是数字" + ch);
              }
              if(char.IsPunctuation(ch))
              {
                    fuhao_count++;
                    Console.WriteLine("第" + weizhi + "位是符号" + ch);
               } 
               weizhi++;
         }
         Console.WriteLine("字母共:"+zimu_count+",数字共:"+shuzi_count+",符号共个:"+fuhao_count);
         Console.ReadLine()

}

第三题:根据输入的小明的java和c#成绩判断进行判断,java和c#都是80分以上,
则输出可以评三好学生,java或者c#有一科是90以上也输出可以评三好学生,否则不可以评为三好学生。

static void Main(string[] args)
{
        Console.WriteLine("请输入小明的java成绩");
        string java_txt = Console.ReadLine();
        Console.WriteLine("请输入小明的C#成绩");
        string c_txt = Console.ReadLine();
        int java_score = Convert.ToInt32(java_txt);
        int c_score = Convert.ToInt32(c_txt);
        if(java_score>=80 && c_score>=80)
        {
              Console.WriteLine("三好学生");
        }
        else if (java_score >= 90 || c_score >= 90)
        {
              Console.WriteLine("三好学生");
        }
        else
        {
                Console.WriteLine("还得努力啊");
        }
}

第二部分 C#语法基础

2.4 条件语句和循环语句
2.4.1 条件语句
2.4.1.1 If—else语句
基本条件判断语句If—else
If(判断表达式)
{
//代码快
}
Else //可选
{
//代码块
}

更复杂的条件语句
If(判断表达式)
{
//代码快
}
Else if(条件表达式)
{
//代码快
}
Else if(条件表达式)
{
//代码快
}
Else
{
//代码快
}

嵌套循环
if(表达式1)
{
if(表达式2)
{
// 表达式2为真时执行……
}
else
{
// 表达式2为假时执行……
}
}
else
{
//表达式1为假时执行……
}

嵌套循环的例子
 机票预定:输出实际机票价格
 原价为4000元
 5-10月为旺季,头等舱打9折,经济舱打7.5折
 其他时间为淡季,头等舱打6折,经济舱打3折

static void Main(string[] args)
{
     int month = 6;
     int price = 100;
     int type = 1;
     if (month >= 5 && month <= 10)  // 旺季 
     {
           if (type == 1) // 头等舱 
           {
             Console.WriteLine("您的机票价格为:{0},{1}", price * 0.9, price * 10);
            }
            else if (type == 2)  // 经济舱 
            {
                    Console.WriteLine("您的机票价格为:{0}", price * 0.75);
             }
        }
         else  // 淡季 
         {
              // …… 
          }
          Console.ReadLine();
 }

2.4.1.2 Switch…case的用法

 条件判断的表达式类型
—— 整型或字符串
 break 语句的要求:
 每个 case 都有 break
 default 也要有 break
 特殊情况:

static void Main(string[] args)
        {
            Console.WriteLine("问武汉是哪个国家的城市?");
            Console.WriteLine("A:美国");
            Console.WriteLine("B:英国");
            Console.WriteLine("C:韩国");
            Console.WriteLine("D:中国");
            string str = Console.ReadLine();
            switch (str)
            { 
                case "A" :
                    Console.WriteLine("回答错误");
                    break;
                case "B":
                    Console.WriteLine("回答错误");
                    break;
                case "C":
                    Console.WriteLine("回答错误");
                    break;
                case "D":
                    Console.WriteLine("你好聪明也");
                    break;
                default :
                    Console.WriteLine("哥哥你没看题目吧");
                    break;            
            }
            Console.ReadLine();
        }

2.4.2 循环结构 2.4.2.1 while循环

基本语法
while ( 循环条件 )
 {   
       循环操作
}
static void Main(string[] args)
{
        int i=1;
        while(i<=30)
        {
              Console.WriteLine(i);
              i++;
        }
        Console.ReadLine();
}


## 2.4.2.2 do…while循环
While循环是先循环后执行,而do…while是先执行后循环
do 
{
//代码块
} 
while ( 循环条件 );

static void Main(string[] args)
{
     int i=1;
     do
     {
            Console.WriteLine(i);
            i++;
      }
      while (i <= 30);
      Console.ReadLine();
}

2.4.2.3 for循环

for ( int i = 0 ; i < 100 ; i++ ) 
{
    Console.WriteLine(i);
}    

2.4.2.4 foreach循环
	foreach 循环用于遍历整个集合或数组 ,其中for是循环的意思,each是每一个的意思
foreach (数据类型 元素(变量) in 集合或者数组)
 {
      //语句
 } 
static void Main(string[] args) 
{
	// 存放字母的个数 
	int countLetters = 0; 
	// 存放数字的个数 
	int countDigits = 0; 
	// 存放标点符号的个数 
	int countPunctuations = 0; 
	// 用户提供的输入 
	string input; 
	Console.WriteLine("请输入一个字符串 "); 
	input = Console.ReadLine(); 
		
	// 声明 foreach 循环以遍历输入的字符串中的每个字符。 
	foreach(char chr in input) 
	 {
		// 检查字母 
		if(char.IsLetter(chr)) 
		countLetters++; 
		// 检查数字 
		if(char.IsDigit(chr)) 
			countDigits++; 
		// 检查标点符号 
		if(char.IsPunctuation(chr)) 
			countPunctuations++; 
	}
	Console.WriteLine(“字母的个数为: {0}", countLetters); 
	Console.WriteLine(“数字的个数为: {0}", countDigits); 
	Console.WriteLine(“标点符号的个数为: {0}", countPunctuations); 
}

2.5 数组 2.5.1 数组的定义

组:是同一种数据类型的一组数据,
这些值存储在连续的内存单元中,便于访问和操作。
数组中所有数据类型必须都是相同的。

数组的定义
// 使用 new 设置大小
// 创建长度为5的整型数组
int[ ] array = new int[5];

// 创建的同时初始化
int[ ] arr = new int[5]{0,1,2,3,4}; //[5]——方括号中的数字决定数组的长度
int[ ] arr = new int[ ]{0,1,2,3,4}; // 省略长度
int[ ] arr = {0,1,2,3,4};

int arr1[ ]; //数据类型 数组名[ ]; 这种写法java里支持,c#不支持

2.5.2 数组的赋值和取值

static void Main(string[] args)
{
	  int count;
	  Console.WriteLine("请输入您要登记的学生人数 ");
	  count=int.Parse(Console.ReadLine());
	  // 声明一个存放姓名的字符串数组,其长度等于提供的学生人数 
	  string[] names = new string[count];
	  // 用一个 for 循环来接受姓名 
	  for(int i=0; i<count; i++)
	  {
		        Console.WriteLine("请输入学生 {0} 的姓名 ",i+1);
		        names[i]=Console.ReadLine();
	       }
	        Console.WriteLine("已登记的学生如下: ");
	        // 用 foreach 循环显示姓名 
	        foreach(string disp in names)
	        {
		        Console.WriteLine("{0}", disp);
	        }
            Console.ReadLine();
 }

获得数组的长度(元素的个数)   
数组名.Length
static void Main(string[] args)
{
        // 循环打印数组元素
        int[] array = new int[5] { 0, 1, 2, 3, 4 };  // 声明并初始化一维数组
        for (int i = 0; i < array.Length; i++)       // 输出数组中的所有元素
        {
              Console.WriteLine(array[i]);
         }
         Console.ReadLine();
}

2.5.3 使用var和数组初始化创建隐形数组

Var是个推断关键字,系统根据数组的数据推段出数组元素的数据类型

private void Form1_Load(object sender, EventArgs e)
{
       var numbers = new[] { 1, 2, 3 };
       var str = new[] { "a", "b" };
       //var cc = new[] {"aa",2 }; 这个写法是错的
}

2.5.4 数组结合循环

static void Main(string[] args)
{
            int[] arr = new int[5];
            int max, min;
            Console.WriteLine("请输入五个数字");
            for (int i = 0; i < 5; i++)
            {
                arr[i] = int.Parse(Console.ReadLine());
            }
            //默认数组第一个数字是目前最大值,也是最小值
            max = arr[0];
            min = arr[0];
            foreach (int i in arr)
            {
                if (i > max)
                {
                    max = i;
                }
                if (i < min)
                {
                    min = i;
                }
            }
            Console.WriteLine("最大值是{0}", max);
            Console.WriteLine("最小值是{0}", min);
            Console.ReadLine();

排序方法

将被排序的记录数组R[1…n]垂直排列,每个记录R[i]看作是重量为R[i].key的气泡。根据轻气泡不能在重气泡之下的原则,从下往上扫描数组R:凡扫描到违反本原则的轻气泡,就使其向上"飘浮"。如此反复进行,直到最后任何两个气泡都是轻者在上,重者在下为止。
冒泡排序是就地排序,且它是稳定的。
1)初始
  R[1…n]为无序区。
(2)第一趟扫描
  从无序区底部向上依次比较相邻的两个气泡的重量,若发现轻者在下、重者在上,则交换二者的位置。即依次比较(R[n],R[n-1]),(R[n- 1],R[n-2]),…,(R[2],R[1]);对于每对气泡(R[j+1],R[j]),若R[j+1].key<R[j].key,则交换 R[j+1]和R[j]的内容。
第一趟扫描完毕时,"最轻"的气泡就飘浮到该区间的顶部,即关键字最小的记录被放在最高位置R[1]上。
(3)第二趟扫描
  扫描R[2…n]。扫描完毕时,"次轻"的气泡飘浮到R[2]的位置上……
最后,经过n-1 趟扫描可得到有序区R[1…n]

注意:
  第i趟扫描时,R[1…i-1]和R[i…n]分别为当前的有序区和无序区。扫描仍是从无序区底部向上直至该区顶部。扫描完毕时,该区中最轻气泡飘浮到顶部位置R[i]上,结果是R[1…i]变为新的有序区。
第一次扫描完后,保证了最大值最最地下,第2次扫描后,保证,最大和次大值在最底下。
所以每次扫描后,下一次的扫描的,会少比较一次


原始数据//4 3 2  1

           // 3 4  2 1
           // 3 2 4 1 
           // 3 2 1 4

           //  2 3 1 4
           // 2 1 3 4
           // 1 2 3 4

static void Main(string[] args)
{
            int[] scores = new int[5];
            int temp;
            Console.WriteLine("输入五个学生的成绩");
            for (int i = 0; i < 5; i++)
            {
                Console.WriteLine("输入第{0}个学生成绩", i + 1);
                scores[i] = int.Parse(Console.ReadLine());
            }
            //开始对成绩进行排序
            for (int i = 0; i < scores.Length - 1; i++) //最多做n-1趟排序
            {
                for (int j = 0; j < scores.Length - 1 - i; j++)
                {
                    if (scores[j] < scores[j + 1])
                    {
                        temp = scores[j];
                        scores[j] = scores[j + 1];
                        scores[j + 1] = temp;
                    }
                }
            }
            Console.WriteLine("排序后的顺序是:");
            foreach (int i in scores)
            {
                Console.WriteLine(i);
            }
            Console.ReadLine();
}

2.6 枚举

2.6.1 枚举的定义和使用
前面我们学了很多数据类型,这些都叫预定义数据类型,因为C#语言已经帮我们预先定义好了的,而枚举是一种简单的自定义数据类型

 枚举用于定义具有一组特定值的数据类型
 枚举以 enum 关键字声明

class Program 
{ 
        public enum Color   //枚举的定义,定义了4个成员
        { 
            Blue,  //默认地0表示蓝色,1表示黄色…
            Yellow, 
            Red, 
            Pink 
        } 
        static void Main(string[] args)   //枚举的使用
        { 
             Console.WriteLine( (int)col);
            Color col = Color.Blue; 
            Console.WriteLine(col); 
        } 
} 

使用枚举类型的好处
1) 枚举使代码更容易维护,更容易阅读(团队开发的时候很重要)
2) 枚举有助于确保给变量指定合法的、规范的范围内的值。

C# 中的枚举包含与值关联的数字
默认情况下,将 0 值赋给枚举的第一个元素,然后对每个后续的枚举元素按 1 递增
在初始化过程中可重写默认值

public enum Color
{
      Blue = 1,
      Yellow = 3,
      Red = 5,
      Pink = 7
 }
static void Main(string[] args)
{
       Color col = Color.Red;
       int i = (int)col;
       Console.WriteLine(i);
       Console.ReadLine();
}

枚举类型是一种简单类型

枚举类型表示互相独立的元素的集合,例:一个房间里的同学,一周里的七天,一年的十二个月,等等。计算机在处理这些问题时,用标准的数据类型(例如整数类型)表示它们很不直观。可以用枚举类型表示这些用数值表示不方便或不直观的一组特殊对象的集合。

比如计算机里存储,星期一,星期二。。。。星期天
怎么存呢,存0,1,2来表示不很直观,直接存星期一,星期二,又不安全,
用枚举,又安全有直观。

public enum a
{
        星期一=2,
        星期二,
        星期三,
        星期四,
        星期五,
        星期六,
        星期天
}
class Program
{
        static void Main(string[] args)
        {
            string aa = a.星期一.ToString();
            string bb = a.星期二.ToString();
            string cc =((a)3).ToString();//可以把范围内的整数转化为枚举的成员
            Console.ReadLine();
        }
 }

数组和枚举练习要求、创建2个数组用于保存10个客户名和客户类型信息 要求:1、利用for循环输入用户名和客户类型 2、客户类型定义为枚举类型VIP1,VIP2,VIP3 3、使用循环输出显示出客户名和客户类型 例如: 客户名 客户类型 张三 VIP3 李四 VIP1 王五 VIP

class Program
 {
        public enum a
        {
            VIP1 = 1,
            VIP2 = 2,
            VIP3 = 3
        }
        static void Main(string[] args)
        {
            string[] name = new string[10];
            a[] types = new a[10];
            for (int i = 0; i < 10; i++)
            {
                Console.WriteLine("请输入第{0}个客户的姓名", i + 1);
                name[i] = Console.ReadLine();
                Console.WriteLine("请输入您的客户类型(⒈VIP1/⒉VIP2/⒊VIP3):", i + 1);
                types[i] = (a)int.Parse(Console.ReadLine());
            }
            Console.WriteLine("客户名" + "\t\t客户类型");
            for (int i = 0; i < 10; i++)
            {
                Console.WriteLine(name[i] + "\t\t" + types[i]);
            }
            Console.ReadLine();
      }
}

2.7 简单数据类型转化

采用tostring()方法

static void Main(string[] args)
{
       int num = 5;
       string aa = num.ToString();
}

**

2.7.2 字符串转化为数值类型

采用Parse方法

static void Main(string[] args)
{
       string str = "11";
       int a = int.Parse(str);
       float b = float.Parse(str);
       double c = double.Parse(str);
}

第三部分:类与对象

3.3.1 创建类的方法
类的定义

[访问修饰符/类修饰符]  Class ClassName 
{
            Class -body;
}

定义一个具体类的例子

class Student 
 { 
        public string name;//学生的名字
        public int age;//学生的年龄
        public void Study() 
        { 
            Console.WriteLine(“我是{0},我要好好学习,天天向上!", name);} 

3.3.2 创建对象和使用的方法

对象是类的实例,是具体的事物,要使用对象必须用 关键字new来实例化出类的一个对象
语法:类名 对象 =new 类名();
实例:Stu s = new Stu();\

3.3.3公有成员和私有成员

当一个类里的变量或方法,用private修辞符来修辞,改变量或方法就变成了私有成员.
当一个类里的变量或方法,用Public修辞,就是公有成员,
公有成员和私有成员的区别
私有成员,不能被本类的外面被调用。公有成员在哪里都可以被调用。(注意类的成员一般只能被对象所调用)
public 公有:用于成员访问限制,允许外部成员访问;
private 私有:用于成员访问限制,不允许外部成员访问,只允许内部成员访问;

3.3.4 构造方法

1、什么是构造方法?
首先,它是一个方法,它是类中 众多方法中的一个。其次,它具有类中其他方法所不具备的一些特性。
2,构造方法的作用是什么
构造方法用于对类的成员变量进行初始化
3,方法相对其他方法有哪些不同?
方法名:类中普通的方法可以根据代码书写者的个人喜好任意取名。但是,构造方法不可以。构造方法的名字必须与类名相同。换句话说,当类中的一个方法名与类名相同时,C#即认为此方法为类的构造方法。
执行顺序:类中的普通方法是在类实例化之后被调用执行的,而构造方法则是在类实例化之时执行的。
ClassName objName = new ClassName();
可以理解为 关键字 new 后面 ClassName() 为类的构造方法。

4、如果类中没有书写构造方法,实例化类时会出错么?
不会。首先,C#中 每个类都有自己的构造方法;即便你在书写类时没有书写构造方法,C#也会创造一个默认的没有参数的构造方法出来,这个默认的构造方法是看不到的,但它是真实存在的
5、一个类中只能由一个构造方法么?
不是的,一个类中可以存在多个构造方法,以不同的参数进行区分(参数个数,或者参数类型),这就构成了构造方法的重载。例如:

using System;
 using System.Collections.Generic;
 using System.Text;
 
 namespace ConsoleApplication1
  {
     class Program
      {
         private Program()
          {
             Console.WriteLine("我是构造函数Program(),我没有参数");
         }
         public Program(string a)
          {
             Console.WriteLine("我是构造函数Program(string a),我的参数是字符");
         }
         public Program(int b)
          {
             Console.WriteLine("我是构造函数Program(int b),我的参数是数值");
         }
         public Program(string a,int b)
          {
             Console.WriteLine("我是构造函数Program(string a,int b),我的参数有两个");
         }
         static void Main(string[] args)
          {
             Program objProgram0 = new Program();
             Program objProgram1 = new Program("123");
             Program objProgram2 = new Program(123);
             Program objProgram3 = new Program("123",123);
             Console.Read();
         }
     }
 }

6,构造方法必须满足以下语法规则:
• 方法名必须与类名相同。
• 不要声明返回类型。

3.3.5 引用类型和值类型

3.3.5.1引用类型和值类型的理解
 C# 中的数据类型分为两个基本类别
 值类型

  • 表示实际数据
  • 直接存储值
  • Int等值类型、char、结构
    引用类型
  • 存储数据的地址
  • 类、接口、数组、字符串
    在这里插入图片描述
    值类型和引用类型原理
private static void Main()
{
         int i;
         MyClass mc;
         i = 5;
}

当一个变量声明之后,就会在栈的内存中分配一块内存给这个变量,至于这块内存多大,里面存放什么东西,就要看这个变量是值类型还是引用类型了。

1, 值类型
如果是值类型,为变量分配这块内存的大小就是值类型定义的大小,存放值类型自身的值(内容)。比如,对于上面的整型变量 i,这块内存的大小就是 4个字节(一个 int型定义的大小),如果执行 i = 5;这行代码,则这块内存的内容就是 5(如图 -1)。
在这里插入图片描述
• 引用类型
在这里插入图片描述
由图 -2可知,变量 mc中存放的是 MyClass实例(对象)的对象引用,如果需要访问 mc实例,系统需要首先从 mc变量中得到实例的引用(在堆中的地址),然后用这个引用(地址)找到堆中的实例,再进行访问。需要至少 2步操作才可以完成实例访问。
3.3.5.2引用类型和值类型的使用
在这里插入图片描述
值类型传递的值本身
值传递时只是传递参数的一个副本,方法内对参数对改变并不会影响原来的值。

static void Main(string[] args)
{
      // 声明一个值类型的整型数据类型
      int val = 100;
      Console.WriteLine("该变量的初始值为  {0}", val);
      Test(val);
      Console.WriteLine("该变量的值此时为  {0}", val);
}
static void Test(int getVal)//值类型传递的是具体的值
{
      int temp = 10;
      getVal = temp * 20;
}

引用传递实际是把参数的地址传递到方法中,方法内对参数对改变会影响原来的值

class Program
{
       static void Main(string[] args)
       {
            DataTypeTest objTest = new DataTypeTest();
            objTest.Val = 100;
            Test(objTest);
            Console.WriteLine("变量的值为  {0}", objTest.Val);
        }
        static void Test(DataTypeTest dataTest)
        {
            int temp = 10;
            dataTest.Val = temp * 20;
        }
    }
class DataTypeTest
{
       public int Val;
}

在这里插入图片描述
装箱实例

static void Main(string[] args)
{
       int i = 123;
       object o = i; // 装箱
        i = 456;  //改变i的内容
        Console.WriteLine("值类型的值为 {0}", i);
        Console.WriteLine("引用类型的值为 {0}", o);
}

装箱和拆箱实例

static void Main(string[] args)
{
          int i = 123;
          object o = i;  // 装箱                     
          int j = (int)o; // 拆箱 
}

在程序中减少装箱和拆箱操作。
为什么要减少装箱和拆箱操作
原因有两个,主要是关于效率:就是对于堆的操作效率比较低。
在这里插入图片描述

第四部分:类与对象二

4.1封装、继承、多态

面向对象语言特点:封装、继承、多态

封装:好比一辆汽车,你学开车的时候只需学会诸如踩油门、刹车,转方向盘即可,无需去了解车的内部原理。(人和汽车是2个类,汽车对外界隐藏细节)
继承:一个类是另一个类的父类,这个类叫子类,子类继承了父类的很多特点(字段和方法)–(继承强调的2个类之间的相同点)
多态:一个父亲的几个孩子,有共同点,也有很多不同点(多态强调2个类不同点)

4.2 ref关键字

值类型做方法参数,不能保存修改后的数据。
如何实现值类型也能保存修改后的数据呢?就需要使用ref关键字,将值类型按引用方式传递
ref 修饰参数:调用方法后,参数值的更改仍然保留
在这里插入图片描述
值类型使用ref前

static void Main(string[] args)
{
      int num1 = 5, num2 = 10;   // 两个数字
      Console.WriteLine("交换前两个数的值分别为:{0}和{1}", num1, num2);
      Swap(num1, num2);   // 交换两个数的值
      Console.WriteLine("交换后两个数的值分别为:{0}和{1}", num1, num2);
}
// 交换两个数的方法
private static void Swap(int num1, int num2)
{
       int temp;      // 中间变量
       temp = num1;
       num1 = num2;
       num2 = temp;
}

值类型使用ref后

static void Main(string[] args)	
{
        int num1 = 5, num2 = 10;   
        Console.WriteLine("交换前两个数的值分别为:{0}和{1}", num1, num2);
         Swap(ref num1,ref num2);           
       Console.WriteLine("交换后两个数的值分别为:{0}和{1}", num1, num2);
}
private static void Swap(ref int num1,ref int num2)
{
        int temp;      // 中间变量
        temp = num1;
        num1 = num2;
        num2 = temp;
}

4.3 out关键字

与ref关键字一样,out关键字也是按引用来传递的.
ref和out的不同点
ref 参数的变量必须最先初始化。而out则不同,out的参数在传递之前不需要初始化
ref侧重修改,out侧重输出

static void Main(string[] args)
{
        int score = 55;
        int scoreResult;
        AddScore(ref  score, out scoreResult);
}
private static void AddScore(ref int score,out int scoreResult)
{
        if (score > 50 && score < 60)
        {
                Console.WriteLine("你的成绩在50-60之间,可以加分。");
                score = 60;
                scoreResult = 60;
         }
         else
         {
                Console.WriteLine("你的成绩不在50-60之间,不可以加分。");
                scoreResult = score;
           }
}

4.4静态方法—static关键字

class Program
 {
        static void Main(string[] args)
        {
            int num1 = 5, num2 = 10;
            mySwap.Swap(ref num1, ref num2);
        }
 }
 class mySwap
 {
        public static void Swap(ref int num1, ref int num2)
        {
            int temp;
            temp = num1;
            num1 = num2;
            num2 = temp;
        }
 }
//静态方法使用类名调用

在这里插入图片描述
4.5静态成员变量
 静态方法和静态成员变量是属于整个类的,不针对某个对象,所以它们是通过类来调用
 静态方法只能访问到静态成员变量,实例变量是访问不了的,也不能使用this关键字,因为this关键字表示正在操作的当前对象,静态的成员不能通过对象访问

4.6重载

几个方法,方法名一样,但参数的个数或者参数的类型不一样,这几个方法叫重载。
在这里插入图片描述
在这里插入图片描述
重载的第二种方式
在这里插入图片描述
构造函数的重载
在这里插入图片描述
类与对象练习实例

class Program
    {
        static void Main(string[] args)
        {
            Console.WriteLine("请输入圆的半径");
            float y_banjing = float.Parse(Console.ReadLine());
            Console.WriteLine("请输入正方形的边长");
            int  zfx_bianchang = int.Parse(Console.ReadLine());
            GetArea area = new GetArea();
            float yuan_area = area.CalculateArea(y_banjing);
            int zfx_area = area.CalculateArea(zfx_bianchang);
            Console.WriteLine("圆的面积是" + yuan_area);
            Console.WriteLine("正方形的面积是"+zfx_area);
            Console.ReadLine();
        }
}

    class GetArea
    {
        public float CalculateArea(float banjing)//计算圆的面积
        {
            return banjing * banjing;
        }
        public int CalculateArea(int  bianchang)//计算正方形的面积
        {
            return bianchang * bianchang;
        }
     
 }

第五部分:深入理解C#OOP

5.1 结构体
结构体是一种和类很相似的数据类型。

类的回顾
类中可以定义成员变量和成员方法,表示该类型对象具有的特征和行为,并且类的对象是在内存中“堆栈”里存放地址,“堆”中存放成员变量的数据,把类对象作为参数传递时,
是把对象在内存中的地址传递给对方,对象属于引用类型。

结构体的定义

访问修辞符  struct  结构体名
{
    定义结构体成员;
}

结构体实例

struct Student
    {
        public string id;
        public int age;
        public string name;
        public void SayHello()
        {
            Console.WriteLine("大家好");
        }
    }
    //结构体也可以有构造函数,与类相似,但只能声明有参数的构造函数

结构体和类的区别
1, 结构体关键字是struct关键字,而类的关键字是class
2, 类的引用类型,而结构体是值类型(把结构体作为参数传递给方法时,是把数据赋值给形参)。
3, 结构体里的成员变量不可以赋初值,类可以。
在这里插入图片描述
结构体的使用

class Program
{
        static void Main(string[] args)
        {
            Student stu;//结构体不需要new关键字创建对象
            stu.id = "1527";
            stu.name = "关羽";
            stu.age = 24;

            Change(stu);
            Console.WriteLine(stu.name);
            Console.WriteLine(stu.age);
            Console.ReadLine();
        }
        public static void Change(Student s)
        {
            s.name = "张飞";
            s.age = 48;
        }
}
struct Student
{
        public string id;
        public int age;
        public string name;
        public void SayHello()
        {
            Console.WriteLine("大家好");
        }
}

5.2属性*

属性的实例代码
在这里插入图片描述
一般使用public来定义成员变量和成员方法,因为我们要在类外面访问它,但这破坏了类的封装性,因为任何成员都可以访问public成员。
但用private来定义成员,只能本类中访问,外界又访问不到,哪如何在类的外面也可以访问私有成员呢?
解决办法就是属性

属性:
实现了保护类成员的同时,允许用户像访问公有成员一样访问属性,简介访问私有成员
比如:班上某个男同学想给某位女同学送礼物,但这个女生不想见男同学,于是男同学
把礼物交给班上另以为女朋友,请她转交给她
属性的定义

访问修辞符  数据类型 属性名
{
     get
     {
         返回私有成员变量;    //用于获得成员变量的值
}
     set
{
       设置私有成员变量;    //设置成员变量的值
}
}	

属性的数据类型与所保护的成员变量的数据类型必须一致
属性的使用

class Program
{
        static void Main(string[] args)
        {
            Person per = new Person(22,"张三");
        }
}
class Person
{
        private  int age;
        private  string name;
        public int Age
        {
            get { return this.age; }  //读取私有成员
            set { this.age = value; } //对私有成员赋值
        }
        public string Name
        {
            get { return this.name; }
            set { this.name = value; }
        }
        public Person(int Age1, string Name1)
        {
            this.age = Age1;
            this.name = Name1;
        }
}
//思考一下,如果只让外界取值,不能赋值如何实现

自动属性
在C#3.0和更高的版本中,可以以更加简介的方式来创建属性

例如:public int age{get;set;}

 class Person
{
       public int Age{get;set;}
       public string Name{get;set;}
}

5.3 索引器

索引器是一种特殊的属性,可以把它添加到一个类中,以提供类似数组的访问,
比如可以访问私有的数组

class Program
 {
       static void Main(string[] args)
       {
           Person per = new Person();
            per[0] = "张三";   //赋值
            per[1] = "李四";
            per[2] = "王五";
            per[3] = "赵六";
            per[4] = "田七";
            string aa = per[4];  //取值
            string bb = per[3];
        }
    }
    class Person
    {
        private string[] all_students = new string[5];
        public string  this[int index]
        {
            get
            {
                if (index < 0 || index >= this.all_students.Length)
                {
                    Console.WriteLine("索引无效");
                    return null;
                }
                return all_students[index];//返回请求的数据
            }
            set
            {
                 //验证索引访问
                              if(index<0||index>=this.all_students.Length)
                 {
                     Console.WriteLine("索引无效");
                     return;//表示失败
                 }
                 this.all_students[index]=value;//向数组加载数据
            }
        }
}

5.4 静态类

静态方法:不需要实例化类对象直接用类名调用的方法。
静态类:只包含静态成员的类

静态类和非静态类的区别
在这里插入图片描述

第六部分:继承和多态

6.1 继承
继承的理解:
比如某父2代继承了父辈的遗产。
继承:是面向对象设计的3大特性之一,它可以实现代码的重用。
继承就是在类与类之间建立一种从属关系,使得新定义的子类(也叫派生类),拥有和父类(也叫基类)一样的成员方法和成员变量。
在这里插入图片描述
继承的语法

Class 类名: 父类类名
{
}
一旦你继承了某个类,那么你将会拥有这个类的成员变量和成员方法。

继承的实例

class Program
{
        static void Main(string[] args)
        {
            student s1 = new student();
            s1.name = "张三";
            s1.Speak();
            s1.study();
            Console.ReadLine();
        }
}
class Person
{
        public int age;
        public string name;
        public void Speak()
        {
            Console.WriteLine("您好很好高兴认识你"+this.name);
        }
}
class student:Person
{
        public string classname;
        public string study()
        {
            Console.WriteLine("我们都很爱学习");
        }
}
class KeXueJia : Person
{
        public string danwei;//属于哪个单位
        public void XieLunWen()
        {
            Console.WriteLine("我们很会写论文呢");
        }
}   
//子类除了可以继承父类的成员变量和成员方法,还可以新增一些成员

6.2 继承中的构造方法

父类构造方法不能被继承。那么子类是如何初始化基类成员的呢
关键字base的作用
1)调用父类的属性和方法
2)调用父类的构造方法

class Program
 {
        static void Main(string[] args)
        {
            student s1 = new student(22,"wew");
            s1.Speak();
            s1.study();
            Console.ReadLine();
        }
    }    
class Person
    {
        public int age;
        public string name;
        public Person(int age1, string name1)
        {
            this.age = age1;
            this.name = name1;
        }
        public void Speak()
        {
            Console.WriteLine("您好很好高兴认识你" + this.name);
        }
    }
    class student : Person
    {
        public string classname;
        public student(int age11,string  name11): base(age11,name11)//base可调父类的构造方法
        { 
        }
        public void study()
        {      
 base.Speak();//base调亲类的方法
            Console.WriteLine(base.name+"我们都很爱学习");//base可调父类的属性
        }
    }

隐式调用父类的构造方法
在这里插入图片描述
显示调用父类构造方法
在这里插入图片描述

6.3 protected访问修饰符

在这里插入图片描述
6.4继承的作用于综述
1.模拟了实现世界中的人物关系
2.实现代码的重用
3.降低代码的耦合度使代码结构清晰,提高开发效率

6.5 密封类

就是没有繁殖能力的类
 如果不希望一个类被其它的类继承,可以把这个类定义为密封类
 定义密封类需要使用sealed关键字

密封类代码实例

sealed class Circle
{
        public int radius;  //圆的半径
        public double GetInfo()
        {
            return 2 * Math.PI * radius;
        }
}
class RedCircle : Circle //密封类错误不能被继承
{

}

6.6 多态

 继承性体现了子类和父类共同的特点和行为,体现了相似性
 多态性则更推进了一步。突出了子类和父类不同的行为,体现了子类和父类的差异性
 在派生于同一个类的不同对象上执行任务时,多态性是一种极为有效的技巧,其使用的代码最少

使用virtual关键字实现多态

public virtual void Func() 
{
	Console.WriteLine("这是 virtual 方法,可以被重写");
}

用多态方式显示员工信息----代码实例

class Program
{
        static void Main(string[] args)
        {
            DervEmployee fr = new DervEmployee();
            fr.EmpInfo();
            Console.ReadLine();
        }
 }
 class Employee
 {
          public virtual void EmpInfo()  //虚方法
          { 
	        Console.WriteLine("此方法显示职员信息"); 
          }
  }
 class DervEmployee: Employee
{
          public override void EmpInfo()  //重写虚方法
          { 
	        Console.WriteLine("此方法重写 base 方法"); 
          }
 }

第七部分:接口的理解和使用

什么是接口?其实,接口简单理解就是一种约定,使得实现接口的类或结构在形式上保持一致
接口的实例
在这里插入图片描述
从接口的定义方面来说,接口其实就是类和类之间的一种协定,一种约束.还拿上面的例子来说.
所有继承了IBark接口的类中必需实现Bark()方法.那么从用户(使用类的用户)的角度来说,如果他知道了某个类是继承于IBark接口,那么他就可以放心大胆的调用Bark()方法,而不用管Bark()方法具体是如何实现的.比如,我们另外写了一个类.

在这里插入图片描述
如果我们从设计的角度来看.一个项目中用若干个类需要去编写,由于这些类比较复杂,工作量比较大,这样每个类就需要占用一个工作人员进行编写.比如A程序员去定Dog类,B程序员去写Cat类.这两个类本来没什么联系的,可是由于用户需要他们都实现一个关于"叫"的方法.这就要对他们进行一种约束.让他们都继承于IBark接口,目的是方便统一管理.

第八部分:简单工厂(类的继承)

 //子类的对象,一旦转化为父类的类型,就不能调用子类所独有的成员
        protected void Button1_Click(object sender, EventArgs e)
        {
            string ss = TextBox1.Text;
            Light lg=  CreateLight("bai"); //调方法创建灯
            //Response.Write(lg.FaGuang());


            Light ls=new Light();
            Response.Write(ls.FaGuang());
        }
        //生产灯的方法
        private static Light CreateLight(string lightname)
        {
             switch(lightname)
             {
                 case "bai":
                       return new baizhideng(); //返回一个白炽灯对象
                 case "ri":
                      return new riguangdeng(); //返回一个日光灯对象
                 default:
                      return null;
             }
        }
    }
    //重写override:是子类对父类的方法的覆盖(重新实现)
    //子类要对父类的方法进行重写,父类方法必须是虚方法(abstract)、或者抽象方法 (virtual)
    //抽象方法:不能有方法的实现(不能有方法体)
    //抽象方法只能存在于抽象类中
    //abstract class Light
    //{
    //    public abstract string FaGuang();
    //}
    interface ILight
    {
         string FaGuang();
    
    }

    class Light
    {
        public virtual string FaGuang()
        {
            return "我是灯,我能发光";
        }
    }
    class baizhideng : Light
    {
        public override string FaGuang()
        {
            return "我是白炽灯,我能发光";
        }
        public string FaRe()
        {
            return "我能发热";
        }
    }
    class riguangdeng : Light
    {
        public override string FaGuang()
        {
            return "我是日光灯灯,我能发光";
        }
    }

}

第九部分:数组、集合、泛型集合综合应用

using System;
using System.Collections.Generic;
using System.Collections;
using System.Linq;
using System.Web;
using System.Web.UI;
using System.Web.UI.WebControls;

namespace Shop_Wan
{
    public partial class WebForm1 : System.Web.UI.Page
    {
        protected void Page_Load(object sender, EventArgs e)
        {
            //1创建一个长度为5的数组,准备存放5个同学的信息(姓名、年龄、性别)
            Student[] stu = new Student[5];

            //2 创建5个学生对象,并赋值
            Student stu1 = new Student("张三", "男", 21);
            Student stu2 = new Student("李四", "男", 21);
            Student stu3 = new Student("王武", "男", 15);
            Student stu4 = new Student("招六", "女", 20);
            Student stu5 = new Student("钱其", "男", 21);
           
            //3:把5个对象放入数组
            stu[0] = stu1;
            stu[1] = stu2;
            stu[2] = stu3;
            stu[3] = stu4;
            stu[4] = stu5;

            //4:使用for循环遍历数组,输出每个同学的姓名
            for (int i=0; i < stu.Length; i++)
            {
                Response.Write(stu[i].name);
            }

            //5:使用foreach遍历数组,输出每个同学的姓名
            foreach (Student a in stu)
            {
                Response.Write(a.name);
            }

            //6:输出数组中第2个同学的姓名和第4个同学的姓名
            Response.Write(stu[1].name);
            Response.Write(stu[3].name);

            //7:把5个对象放入集合,并遍历,输出每个同学的姓名和年龄
            ArrayList ar = new ArrayList();
            ar.Add(stu1);
            ar.Add(stu2);
            ar.Add(stu3);
            ar.Add(stu4);
            ar.Add(stu5);
            for (int i = 0; i < ar.Count; i++)
            {
                Student s = ar[i] as Student;
                Response.Write(s.name+s.age);
            }

            //8:把5个对象放入泛型集合,并遍历泛型,输出每个同学的姓名和年龄
            List<Student> ls=new List<Student>();
            ls.Add(stu1);
            ls.Add(stu2);
            ls.Add(stu3);
            ls.Add(stu4);
            ls.Add(stu5);
            foreach (Student b in ls)
            {
                Response.Write(b.name+b.age);
            }
        }
    }
}

Student.cs
using System;
using System.Collections.Generic;
using System.Linq;
using System.Web;

namespace Shop_Wan
{
    public class Student
    {
        public string name { set; get; }
        public string sex { set; get; }
        public int  age { set; get; }
        public  Student(string name1,string sex1,int age1)
        {
            name = name1;
            sex = sex1;
            age = age1;
        }
    }
}

第十部分:C#常用类

Math类
Math类是一个静态类,它是通用数学函数提供常数和静态方法

成员: 
________________________________________
/* 字段 */
Math.PI;            //3.14159265358979

/* 静态方法 */
Math.Abs;           //绝对值
Math.Acos;          //反余弦
Math.Asin;          //反正弦
Math.Atan;          //反正切
Math.Atan2;         //反正切, 两参数
Math.BigMul;        //int32 * int32 = int64
Math.Ceiling;       //取 >= 的最小整数
Math.Cos;           //余弦
Math.Cosh;          //双曲余弦
Math.DivRem;        //取商和余数
Math.Exp;           //求 e 的指定次幂
Math.Floor;         //取 <= 的最大整数
Math.IEEERemainder; //求余
Math.Log;           //自然对数
Math.Log10;         //以 10 为底的对数
Math.Max;           //取大
Math.Min;           //取小
Math.Pow;           //求幂
Math.Round;         //就近舍入
Math.Sign;          //取符号, 或返回 -1、0、1 
Math.Sin;           //正弦
Math.Sinh;          //双曲正弦
Math.Sqrt;          //平方根
Math.Tan;           //正切
Math.Tanh;          //双曲正切
Math.Truncate;      //取整

Random类
Random类是一个产生随机数字的类。
它的构造函数有两种,一个是直接New Random(),另外一个是New Random(Int32),前者是根据触发那刻的系统时间做为种子,来产生一个随机数字,后者可以自己设定触发的种子,一般都是用UnCheck((Int)DateTime.Now.Ticks)做为参数种子。

方法 说明
Next 有方法重载,返回随机数
NextBytes 用随机数填充指定字节数组的元素
NextDouble 返回一个介于0.0和1.0之间的随机数
ToString 返回表示当前Object的String
在这里插入图片描述
DataTime类专门用于获取时间

string aa= DateTime.Now.ToLongDateString();//获得当前的年月日
string aa2 = DateTime.Now.Date.ToShortDateString();//获得当前的年月日(和前面格式不一样)
string bb = DateTime.Now.Add(TimeSpan.FromMinutes(30)).ToString();//30分钟的时间
string cc = DateTime.Now.AddDays(22).ToString();//22天的日期
string dd = DateTime.Now.AddHours(2).ToString();//2小时后的日期
string ee = DateTime.Now.AddMinutes(20).ToString();//20分钟后的时间
string ff = DateTime.Now.AddYears(20).ToString();//20年后的时间
string gg = DateTime.Now.Date.DayOfWeek.ToString();//星期几
string hh = DateTime.Now.Day.ToString();//今天是几号
string ii = DateTime.Now.Date.DayOfYear.ToString();//今天是本年的多少天
string jj = DateTime.Now.Date.Month.ToString();//当前是几月份
string kk = DateTime.Now.CompareTo(DateTime.Now).ToString();//比较大小0则是等于,1是小于,-1是大于
string ll = DateTime.Now.AddMilliseconds(100000).ToString();//1000000毫秒之后
string mm = DateTime.IsLeapYear(2033).ToString();//某年是否是闰年

DateTime dt = DateTime.Now;
dt.ToString();//2005-11-5 13:21:25
dt.ToFileTime().ToString();//127756416859912816
dt.ToFileTimeUtc().ToString();//127756704859912816
dt.ToLocalTime().ToString();//2005-11-5 21:21:25
dt.ToLongDateString().ToString();//2005年11月5日
dt.ToLongTimeString().ToString();//13:21:25
dt.ToOADate().ToString();//38661.5565508218
dt.ToShortDateString().ToString();//2005-11-5
dt.ToShortTimeString().ToString();//13:21
dt.ToUniversalTime().ToString();//2005-11-5 5:21:25
dt.Year.ToString();//2005
dt.Date.ToString();//2005-11-5 0:00:00
dt.DayOfWeek.ToString();//Saturday
dt.DayOfYear.ToString();//309
dt.Hour.ToString();//13
dt.Millisecond.ToString();//441
dt.Minute.ToString();//30
dt.Month.ToString();//11
dt.Second.ToString();//28
dt.Ticks.ToString();//632667942284412864
dt.TimeOfDay.ToString();//13:30:28.4412864
dt.ToString();//2005-11-5 13:47:04
dt.AddYears(1).ToString();//2006-11-5 13:47:04
dt.AddDays(1.1).ToString();//2005-11-6 16:11:04
dt.AddHours(1.1).ToString();//2005-11-5 14:53:04
dt.AddMilliseconds(1.1).ToString();//2005-11-5 13:47:04
dt.AddMonths(1).ToString();//2005-12-5 13:47:04
dt.AddSeconds(1.1).ToString();//2005-11-5 13:47:05
dt.AddMinutes(1.1).ToString();//2005-11-5 13:48:10
dt.AddTicks(1000).ToString();//2005-11-5 13:47:04
dt.CompareTo(dt).ToString();//0
dt.Equals("2005-11-6 16:11:04").ToString();//False
dt.Equals(dt).ToString();//True
dt.GetHashCode().ToString();//1474088234
dt.GetType().ToString();//System.DateTime
dt.GetTypeCode().ToString();//DateTime

dt.GetDateTimeFormats("s")[0].ToString();//2005-11-05T14:06:25
dt.GetDateTimeFormats("t")[0].ToString();//14:06
dt.GetDateTimeFormats("y")[0].ToString();//2005年11月
dt.GetDateTimeFormats("D")[0].ToString();//2005年11月5日
dt.GetDateTimeFormats("D")[1].ToString();//2005 11 05
dt.GetDateTimeFormats("D")[2].ToString();//星期六 2005 11 05
dt.GetDateTimeFormats("D")[3].ToString();//星期六 2005年11月5日
dt.GetDateTimeFormats("M")[0].ToString();//11月5日
dt.GetDateTimeFormats("f")[0].ToString();//2005年11月5日 14:06
dt.GetDateTimeFormats("g")[0].ToString();//2005-11-5 14:06
dt.GetDateTimeFormats("r")[0].ToString();//Sat, 05 Nov 2005 14:06:25 GMT
string.Format("{0:d}",dt);//2005-11-5
string.Format("{0:D}",dt);//2005年11月5日
string.Format("{0:f}",dt);//2005年11月5日 14:23
string.Format("{0:F}",dt);//2005年11月5日 14:23:23
string.Format("{0:g}",dt);//2005-11-5 14:23
string.Format("{0:G}",dt);//2005-11-5 14:23:23
string.Format("{0:M}",dt);//11月5日
string.Format("{0:R}",dt);//Sat, 05 Nov 2005 14:23:23 GMT
string.Format("{0:s}",dt);//2005-11-05T14:23:23
string.Format("{0:t}",dt);//14:23
string.Format("{0:T}",dt);//14:23:23
string.Format("{0:u}",dt);//2005-11-05 14:23:23Z
string.Format("{0:U}",dt);//2005年11月5日 6:23:23
string.Format("{0:Y}",dt);//2005年11月
string.Format("{0}",dt);//2005-11-5 14:23:23
string.Format("{0:yyyyMMddHHmmssffff}",dt);

计算2个日期之间的天数差

DateTime dt1 = Convert.ToDateTime("2007-8-1");
DateTime dt2 = Convert.ToDateTime("2007-8-15");
TimeSpan span = dt2.Subtract(dt1);
int dayDiff = span.Days + 1;

计算某年某月的天数

int days = DateTime.DaysInMonth(2007, 8);
days = 31;      
//给日期增加一天、减少一天
DateTime dt = DateTime.Now;
dt.AddDays(1); //增加一天
dt.AddDays(-1);//减少一天

第十一部分:String类的常用方法

把指定字符串插入到指定的位置

string str = "abcde";
str = str.Insert(2,"vvv");

字符串的分割与获取
String类提供了一个方法Split用以切割字符串,此方法返回一个包含所有分割子字符串的数组对象,可以经过数组取得所有分割的子字符串

 string str = "How.are you:what's. you name:I. love c#";
string result = "";
char[] chs = new char[] { ':', '.' };
foreach (string s in str.Split(chs))
{
       result += s + '\n';//遇到“:”和“。”进行切割
}
string[] ss= str.Split(chs);//切割后形成一个数组

在字符串中截取一段字符串

string str = "abcde";
str = str.Substring(2);

第十二部分:StringBuild的用法
StringBuild类不具有String类的强大功能,但可以进行添加、删除和替换字符串中的文本,工作效率很高

//(1)定义方式
string str =Hello World”;
StringBuilder sb = new StringBuilder("Hello World");

//(2)显示整个字符串的时候显示方式
Console.WriteLine("{0}",str);
Console.WriteLine("{0}",sb.Tostring());
在末端添加文本:sb.Append("***"); //***为所添加的内容
获取最大文本数量:sb.MaxCapacity 

String类和stringbuild类的使用区别

static void Main(string[] args)
{
     string str = " Hello World";
     Console.WriteLine("显示{0}", str);
     Console.WriteLine("第一个{0}", str[0]);
     Console.WriteLine("第二个{0}", str[1]);
     Console.WriteLine("插入字符{0}",str.Insert(6," C#"));
     Console.WriteLine("字符串长度{0}", str.Length);
     Console.WriteLine("删除字符串空格{0}", str.Trim());
     Console.WriteLine("字符串复制{0}",str.Clone().ToString());
     Console.WriteLine("小写字符串{0}", str.ToLower());
     Console.WriteLine("大写字符串{0}", str.ToUpper());
     Console.WriteLine("截取部分字符串{0}", str.Substring(1, 6));

     StringBuilder sb = new StringBuilder("Hello World ");
     Console.WriteLine("显示{0}", sb.ToString());
     Console.WriteLine("末端追加文本{0}", sb.Append("!!!"));
     Console.WriteLine("指定位置添加文本{0}", sb.Insert(6, "dear "));
     Console.WriteLine("最大文本数量{0}", sb.MaxCapacity);
     Console.ReadLine();
}
  • 0
    点赞
  • 10
    收藏
    觉得还不错? 一键收藏
  • 打赏
    打赏
  • 0
    评论
评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包

打赏作者

LilyCoder

你的鼓励将是我创作的最大动力

¥1 ¥2 ¥4 ¥6 ¥10 ¥20
扫码支付:¥1
获取中
扫码支付

您的余额不足,请更换扫码支付或充值

打赏作者

实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

1.余额是钱包充值的虚拟货币,按照1:1的比例进行支付金额的抵扣。
2.余额无法直接购买下载,可以购买VIP、付费专栏及课程。

余额充值