C#学习笔记

一、学前入门

1. .Net平台

.Net/DotNet:一般指.Net FrameWork框架,一种平台,一种技术。

为了方便理解,打个比方:

.Net平台 = 一个厨房

.Net FrameWork框架 = 柴米油盐酱醋茶各类调料、锅碗瓢盆各类工具

基于.Net平台开发出来的各种应用 = 一道道好吃/难吃的饭菜

总结:.Net FrameWrok框架是.Net平台中不可缺少的一部分,它提供了一个稳定的运行环境来保证我们基于.Net平台开发的各种应用能够正常的运转。

2. C#编程语言

C#(C Sharp):一种编程语言,可以开发基于.Net平台的应用。

3. .Net都能干什么

1.Winform应用程序(桌面应用程序)

2.Internet应用程序(ASP.NET)

3.手机开发(wp8开发)

4.Unity3D游戏开发/虚拟现实开发

4. .Net两种交互模式

1. C/S:客户端(Client)/服务器(Server)模式

2. B/S:浏览器(Browser)/服务器(Server)模式

5. VS的各个组成部分

解决方案、项目及类之间的关系:

解决方案 = 公司

项目 = 部门

类 = 员工

方法(函数)= 员工会的技能

Program中的各个组成部分:

引用命名空间;

方法(函数);

注意:Main函数是我们程序的主入口,你写的代码如果想要被执行的话,必须写在Main函数当中。

.sln:解决方案文件,里面包含着整个解决方案的信息,可以双击运行。

.csproj:项目文件,里面包含着这个项目的信息,可以双击运行。

6.书写代码需要注意的地方

(1)代码中出现的所有标点都是英文半角,Shift键快速切换中文半角和英文半角,

    Shift+空格切换全角/半角。

(2)在C#代码中,每行代码的结束,我们都以分号结束,注意:这个分号也是英文半角的分号。

7.运行程序的两种方式

(1)点击绿色的启动按钮;

(2)使用快捷键F5。

8. VS中常用的快捷键

(1)Ctrl+K+D:快速对齐代码;

(2)Ctrl+Z:撤销;

(3)Ctrl+S:保存;

(4)Ctrl+J:快速弹出智能提示;

(5)Shift+End、Shift+Home

(6)Ctrl+K+C:注释所选代码;

(7)Ctrl+K+U:取消对所选代码的注释;

(8)F1:转到帮助文档;

(9)折叠冗余代码:#Region和#EndRegion;

二、基础语法

1.注释符

注释符的作用:

(1)用来注销代码段;

(2)用来解释代码段;

C#的三种注释符:

(1)单行注释  //  

(2)多行注释  /* 要注释的内容  */  

(3)文档注释  ///            多用来解释类或方法。

例1:

//这行代码的作用是将Hello World打印到控制台当中
Console.WriteLine("Hello World");

//这行代码的作用是暂停当前程序
Console.ReadKey();

例2:

/// 
    /// 这个方法的作用是:求两个整数之间的最大值
    /// 
    /// 第一个整数
    /// 第二个整数
    /// 返回比较大的那个数字
    public static int GetMax(int n1,int n2)
    {
        return n1 > n2 ? n1 : n2;
    }

例3:

/// 
    /// 这个类用来描述一个人的信息:姓名、年龄、性别
    /// 
    public class Person
    {
        public string Name
        {
            get;
            set;
        }

        public int Age 
        {
            get;
            set;
        }

        public char Gender
        {
            get;
            set;
        }
    }

2. 变量

1.变量的作用

用来在计算机当中存储数据。

2.存储变量的语法

变量类型  变量名;

变量名=值;

注意:“=”号,在这里并不表示等于的意思,而是赋值的意思,表示把等号右边的值 赋给等号左边的变量。

例:

//官方语言:声明或定义了一个int类型的变量
        int number;//在内存中开辟了一块能够存储整数的空间
        //官方语言:给这个变量进行赋值
        number = 100;//表示把100存储到了这块空间内
    }

3.数据类型

类型 范围 大小 .Net Framework类型
int -2147483648到2147483647 有符号  32位整数 System.Int32

(1)整数类型:int  只能存储整数,不能存储小数。

(2)小数类型:double  既能存储整数,也能存储小数,小数点后面的位数为 15~16位。

(3)金钱类型:decimal  用来存储金钱,值后面需要加上一个m。

(4)字符串类型:string  用来存储多个文本,也可以存储空,字符串类型的值需要被双引号(英文半角)括起来。

(5)字符类型:char  用来存储单个字符,最多、最少只能有一个字符,不能存储空,字符类型的值需要用单引号(英文半角)括起来。

4. 变量的使用规范

如果你要是用变量的话,应该要先声明再赋值再使用;

5. 变量的命名规则

(1)必须以 字母、下划线_  或@符号开头,不要以数字开头;

(2)后面可以跟任意 字母、数字、下划线。

注意:

(1)你起的变量名不要与C#系统中的关键字重复;

(2)在C#中,大小写是敏感的;

(3)同一个变量名不允许重复定义。

给变量起名字的时候要满足两个命名规范:

(1)Camel 骆驼命名规范,要求变量名称首单词的首字母要小写,其余每个单词的首字母要大写,多用于给变量命名;

(2)Pascal 命名规范,要求每个单词的首字母都要大写,其余字母小写,多用于给类或者方法命名。

6. 赋值运算符

= 表示赋值的意思,表示把等号右边的值,赋值给等号左边的变量。由等号连接的表达式称之为赋值表达式。例如: int number=10;

注意:每个表达式我们都可以求解除一个定值,对于赋值表达式而言,等号左边的变量的值就是整个赋值表达式的值。

7. +号的作用

(1)连接:当+号两边有一边是字符串的时候,+号就起到了连接的作用;

(2)相加:当两边都是数字的时候;

C#学习笔记_第1张图片

 答案如下:

static void Main(string[] args)
    {
        //第一题答案
        string name = "卡卡西";
        string address = "火影村";
        int age = 30;
        string email = "[email protected]";
        int salary = 2000;
        Console.WriteLine("我叫"+name+",我住在"+address+",我今年"+age+"了,我的邮箱是"+email+",我的工资是"+salary);

        //第二题答案
        int myAge = 18;
        myAge = 81;
        Console.WriteLine("这个人的年龄是:" + age + "岁");
    }

8. 占位符

使用方法:先挖个坑,再填个坑。

使用占位符需要注意的地方:

(1)你挖了几个坑,就需要填上几个坑,如果你多填了,没效果;如果你少填了,就会抛异常;

(2)输出顺序:按照挖坑的顺序输出;

C#学习笔记_第2张图片

 答案如下:

static void Main(string[] args)
    {
        //第三题答案
        string name = "小明";
        int age = 18;
        char gender = '男';
        string phoneNumber = "010-12345";
        Console.WriteLine("我叫{0},我今年{1}岁了,我是{2}生,我的电话是{3}", name, age, gender, phoneNumber);

        //第四题答案
        string myAddress = "芒夏路19号";
        int myAge = 15;
        string myName = "丧彪";
        Console.WriteLine("我家在{0},今年{1}岁了,我的姓名是{2}", myAddress, myAge, myName);

        //第五题答案一
        int number1 = 10;
        int number2 = 5;
        int temp;
        temp = number1;
        number1 = number2;
        number2 = temp;
        Console.WriteLine("交换后number1的值为{0},number2的值为{1}", number1, number2);
        
        //第五题答案二
        int numberA = 10;
        int numberB = 5;
        numberA = numberA + numberB;
        numberB = numberA - numberB;
        numberA = numberA - numberB;
        Console.WriteLine("交换后numberA的值为{0},numberB的值为{1}", numberA, numberB);
    }

9.接收用户的输入

static void Main(string[] args)
    {
        Console.WriteLine("请输入你的姓名");
        //我们还想要接收你输入的姓名
        string name = Console.ReadLine();

        Console.WriteLine("您的姓名是{0}", name);
    }

练习:请用户输入姓名、性别、年龄,当用户按下某个键后,在屏幕上显示:您好,xx您的年龄是xx,你是个x生。

答案如下:

static void Main(string[] args)
    {
        Console.WriteLine("请输入你的姓名:");
        string name = Console.ReadLine();
        Console.WriteLine("请输入你的性别:");
        string gender = Console.ReadLine();
        Console.WriteLine("请输入你的年龄");
        string age = Console.ReadLine();
        Console.WriteLine("你好{0},你的性别是{1},你的年龄是{2}", name,gender,age);
    }

10. 转义字符和@符号的作用

转义符:指的是一个斜杠 ‘ \ ’ + 一个特殊的字符,组成了一个具有特殊意义的字符。

\n 表示:换行;

\" 表示:一个英文半角的双引号;

\t 表示:一个Tab键的空格;(多用于排版)

\b 表示:一个退格键,放到字符串的两边没有效果;

\r\n 表示:Windows操作系统不认识\n,只认识\r\n;

\\ 表示:一个\;

@符号作用:

1.取消斜杠\在字符串中的转义作用,使其单纯的表示为一个\;

2.将字符串按照编辑的原格式输出;

11. 强制类型转换

C#学习笔记_第3张图片

答案如下:

static void Main(string[] args)
    {
        //练习2答案
        int r = 5;
        double area_Circle = MathF.PI * r * r;
        double girth_Circle = MathF.PI * r * 2;
        Console.WriteLine("该圆的面积为{0},周长为{1}", area_Circle, girth_Circle);

        //练习3答案
        int price_Tshirt = 35;
        int price_Trousers = 120;
        double price_Total = (3 * price_Tshirt + 2 * price_Trousers)*0.88;
        Console.WriteLine("客户应该支付{0}元", price_Total);
    }

1. 隐式类型转换:

我们要求等号两边参与运算的操作数的类型必须一致,如果不一致,满足以下条件就会发生自动类型转换,或者称之为隐式类型转换。

(1)两种类型相兼容;例如:int 和double兼容(都是数字类型)

(2)目标类型大于源类型;例如:double > int

2. 显式类型转换:

(1)两种类型相兼容;  例如:int 和double兼容(都是数字类型)

(2)大的转成小的; double--int

3. Convert类型转换:

如果两个类型的变量不兼容,比如string与int或者string与double,这个时候我们可以使用一个叫做Convert的转换工厂进行转换。

例如:string s="123";  double d=Convert.ToDouble(s);  Console.WriteLine(d);

注意:使用Convert进行类型转换,也需要满足一个条件:面子上一定得过得去。

例如下面的强制转换就不行:string s="123abc";  double d=Convert.ToDouble(s);  Console.WriteLine(d);  这个123abc就说不过去。

三、运算符

1. 算术运算符

++: 分为前++和后++,不管是前++还是后++,最终的结果都是给这个变量加一;

--: 分为前--和后--,不管是前--还是后--,最终的结果都是给这个变量减一;

区别表现表达式当中,

如果是前++/前--,则先给这个变量自身加一/减一,然后带着这个加一/减一后的值参与运算;

如果是后++/前--,则先拿原值参与运算,运算完成后,再将这个变量自身加一/减一;

对于像++或--这样只需要一个操作数就能完成的运算,我们称之为一元运算符;

+ - *  / % 对于这些需要两个或两个以上才能完成运算的操作符,我们称之为二元运算符。

注意:一元运算符的优先级要高于二元运算符,所以在一个表达式当中,如果既有一元运算符 又有二元运算符,我们首先计算一元运算符。

练习1:

static void Main(string[] args)
    {
        int a = 5;
        int b = a++ + ++a * 2 + --a + a++;
        Console.WriteLine(a);
        Console.WriteLine(b);
    }

输出结果为:

C#学习笔记_第4张图片

练习2:

static void Main(string[] args)
    {
        int var1,var2 = 5, var3 = 6;
        var1 = var2++ * --var3;
        //var1 = ++var2 * var3--;
        Console.WriteLine(var1);
    }

分别输出的结果为:

25

36

2. 关系运算符

6种关系运算符:>   <    >=   <=   ==   !=

关系运算符是用来描述两个事物之间的关系。

由关系运算符连接的表达式称之为关系表达式。

3. 逻辑运算符

3种逻辑运算符:&&(与)、| |(或)、!(非)

逻辑运算符两边放的一般都是关系表达式或者bool类型的值。

由逻辑运算符连接的表达式称之为逻辑表达式。

练习1:

让用户输入语文和数学成绩,输出以下判断是否正确,正确输出true,错误输出false:

(1)输入的语文和数学成绩都大于90分;

(2)语文和数学有一门是大于90分的。

答案如下:

static void Main(string[] args)
    {
        Console.WriteLine("请输入语文成绩:");
        double score_Chinese = Convert.ToDouble(Console.ReadLine());
        Console.WriteLine("请输入数学成绩:");
        double score_Math = Convert.ToDouble(Console.ReadLine());
        bool isTrue;
        //isTrue = score_Chinese > 90 && score_Math > 90;
        isTrue = score_Chinese > 90 || score_Math > 90;
        Console.WriteLine(isTrue);
    }

练习2:

判断闰年?(闰年的判定需符合以下两个条件中任意的一个即可:1. 年份能够被400整除;2. 年份能够被4整除但是不能被100整除)

static void Main(string[] args)
    {
        Console.WriteLine("请输入要判断的年份:");
        int year = Convert.ToInt32(Console.ReadLine());
        bool isRunNian;
        isRunNian = (year% 400 == 0||(year%4==0&&year%100!=0));
        Console.WriteLine(isRunNian);
    }

注意:这里即便不用括号,逻辑也是对的,因为逻辑与的优先级要高于逻辑或。

4.复合赋值运算符

+=  -=  *=   /=   %=

C#学习笔记_第5张图片

四、流程控制

顺序结构:程序从Main函数进入,从上到下 一行一行的执行,不会落下任何一行;

分支结构:if    if—else

选择结构:if    if—else   switch—case

循环结构:while   do—while   for   foreach

1.if语句

语法:

if(判断条件)

{

        要执行的代码;

}

判断条件:一般为关系表达式或者bool类型的值。

执行过程:程序运行到if处,首先判断if所带的小括号中的判断条件,如果条件成立(也就是返回true),则执行if所带的大括号中的代码,如果判断条件不成立(也就是返回false),则跳过if结构,继续向下执行。

if结构特点:先判断,再执行,有可能一行代码都不执行。

2. if—else

语法:

if(判断条件)

{

      执行的代码;

}

else

{

}

执行过程:程序执行到if处,首先判断if所带的小括号中的判断条件是否成立,如果成立(也就是返回true),则执行if所带的大括号中的代码,执行完成后,跳出if-else结构。

如果if判断条件不成立(返回false),则跳过if语句,执行else所带的大括号中的语句,执行完成后,跳出if-else结构。

if-else 特点:先判断,再执行,最少要执行一条代码。

C#学习笔记_第6张图片

 答案如下:

static void Main(string[] args)
    {
        //练习1
        Console.WriteLine("请输入密码:");
        string password = Console.ReadLine();
        if (password == "88888")
        {
            Console.WriteLine("密码输入正确!");
        }
        else
        {
            Console.WriteLine("密码输入错误,请再次输入:");
            password = Console.ReadLine();
            if (password == "88888")
            {
                Console.WriteLine("密码输入正确!");
            }
            else
            {
                Console.WriteLine("输入错误,程序结束");
            }
        }

        //练习2
        //Console.WriteLine("请输入用户名:");
        //string account = Console.ReadLine(); 
        //Console.WriteLine("请输入密码:");
        //string password = Console.ReadLine();
        //if(account=="admin"&&password=="88888")
        //{
        //    Console.WriteLine("输入正确!");
        //}
        //else if(account!="admin")
        //{
        //    Console.WriteLine("用户名不存在");
        //}
        //else if (account == "admin"&&password!="88888")
        //{
        //    Console.WriteLine("密码输入错误");
        //}

        //练习3
        Console.WriteLine("请输入年龄:");
        int age = Convert.ToInt32(Console.ReadLine());
        if(age>=18)
        {
            Console.WriteLine("可以查看");
        }
        else if(age<10)
        {
            Console.WriteLine("不允许查看");
        }
        else
        {
            Console.WriteLine("是否继续查看?(yes/no)");
            string shiFouChaKan = Console.ReadLine();
            if(shiFouChaKan=="yes")
            {
                Console.WriteLine("请查看");
            }
            else
            {
                Console.WriteLine("退出,您已放弃查看");
            }
        }
    }

3. 异常捕获

语法上没有错误,在程序运行的过程当中,由于某些原因程序出现了错误,不能再正常运行。

我们在程序中经常会出现各种各样的异常,你如果想要你的程序变得坚强一些,在你的代码中就应该经常性的使用try-catch来进行异常捕获。

哪段代码你觉得有可能出现异常,你就踹他一脚。

语法:

try
{
    可能会出现异常的代码;
}
//try和catch之间不能有其他的代码
catch
{
    出现异常后要执行的代码;
}

执行过程:如果try中的代码没有出现异常,那么catch中的代码就不会执行。

如果try中的代码出现了异常,哪怕这行出现异常的代码后面还有一百行 都不会执行了,而是直接跳到catch中执行代码。

例子:

static void Main(string[] args)
    {
        bool isNormal = true;
        int number = 0;
        Console.WriteLine("请输入一个数字:");
        try
        {
            number = Convert.ToInt32(Console.ReadLine());
        }
        catch
        {
            Console.WriteLine("异常!!输入的内容不能够转换成数字!");
            isNormal = false;
        }

        //我们如果要执行下面这行代码,需要满足某些条件
        //让代码满足某些条件去执行的话,使用bool类型
        if (isNormal)
        {
            Console.WriteLine("程序正常执行,未发生异常");
        }
    }

4. Switch-case结构

用来处理多条件的定值判断。

语法:

switch(变量或者表达式的值)
{
   case 值1:要执行的代码;
   break;
   case 值2:要执行的代码;
   break;
   case 值3:要执行的代码;
   break;
   ........
   default:要执行的代码;
   break;
}

执行过程:程序执行到switch处,首先将括号中变量或者表达式的值计算出来,然后拿着这个值依次跟每个case后面所带的值进行匹配,一旦匹配成功,则执行该case所带的代码,执行完成后,遇到break,跳出switch-case结构。

如果跟每个case所带的值都不匹配,就看当前这个switch-case结构中是否存在default,如果有default,则执行default中的语句,如果没有default,则该switch-case什么都不做。

练习:

请用户输入年份,再输入月份,输出该月的天数。

static void Main(string[] args)
    {
        try
        {
            Console.WriteLine("请输入年份:");
            int year = Convert.ToInt32(Console.ReadLine());
            try
            {
                Console.WriteLine("请输入月份:");
                int month = Convert.ToInt32(Console.ReadLine());
                switch (month)
                {
                    case 1:
                    case 3:
                    case 5:
                    case 7:
                    case 8:
                    case 10:
                    case 12:
                        Console.WriteLine("本月共31天");
                        break;
                    case 4:
                    case 6:
                    case 9:
                    case 11:
                        Console.WriteLine("本月共30天");
                        break;
                    case 2:
                        if ((year % 400 == 0) || (year % 4 == 0 && year % 100 != 0))
                        {//闰年
                            Console.WriteLine("本月共29天");
                        }
                        else
                        {//平年
                            Console.WriteLine("本月共28天");
                        }
                        break;
                    default:
                        Console.WriteLine("输入的月份不符合实际,程序退出");
                        break;
                }
            }//try月份结尾
            catch
            {
                Console.WriteLine("月份输入有误,程序退出");
            }
        }//try年份结尾
        catch
        {
            Console.WriteLine("年份输入有误,程序退出");
        }
    }

5. 循环结构

(1)while循环

语句:

while(循环条件)
{
   循环体;
}

执行过程:程序运行到while处,首先判断while所带的小括号内的循环条件是否成立,如果成立的话,也就是返回一个true,则执行循环体,执行完一遍循环体后,再次回到循环条件进行判断,如果依然成立,则继续执行循环体,如果不成立,则跳出while循环。在while循环当中,一般总会有那么一行代码,能够改变循环条件,使之终有一天不再成立,如果没有那么一行代码能够改变循环条件,也就是循环条件永远都成立,我们称这种循环叫做死循环。

最简单、最常用的死循环:

while(true)
{      
}

练习:求1-100之间所有整数的和。

static void Main(string[] args)
    {
        int i = 1;
        int sum = 0;
        while(i<=100)
        {
            sum += i;
            i++;
        }
        Console.WriteLine("1-100之间整数的和为:" + sum);
    }

(2)do-while循环

语法:

do

{

     循环体;

}

while(循环条件);

执行过程:程序首先会执行do中的循环体,执行完成后,去判断do-while循环的循环条件,如果成立,则继续执行do中的循环体,如果不成立,则跳出do-while循环。

特点:先循环,再判断,最少执行一遍循环体。

6.程序调试

作用:

(1)写完一段程序后,想看一下这段程序的执行过程;

(2)当你写完这段程序后,发现程序没有按照你想象的样子去执行。

调试方法:

(1)F11 逐语句调试(单步调试)

(2)F10 逐过程调试

(3)断点调试

7. break

1. 可以跳出switch-case结构;

2. 可以跳出当前循环;

注意:break一般不单独的使用,而是跟着if判断一起使用,表示当满足某些条件的时候,就不再循环了。

C#学习笔记_第7张图片

 答案如下:

static void Main(string[] args)
    {
        //练习2:
        //Console.WriteLine("请输入班级人数:");
        //int count = Convert.ToInt32(Console.ReadLine());
        //int i = 0;
        //double score_Average, score_Sum = 0;
        //while (i < count)
        //{
        //    Console.WriteLine("请依次输入每位学员的成绩:");
        //    double score = Convert.ToDouble(Console.ReadLine());
        //    score_Sum += score;
        //    i++;
        //}
        //score_Average = score_Sum / count;
        //Console.WriteLine("该班级总分为:{0},平均分为:{1}", score_Sum, score_Average);

        //练习3
        //int count_Teach = 0;
        //while (count_Teach < 10)
        //{
        //    Console.WriteLine("这道题你们会做了吗?(yes/no)");
        //    string answer = Console.ReadLine();
        //    if (answer == "yes")
        //    {
        //        break;
        //    }
        //    count_Teach++;
        //}
        //Console.WriteLine("放学");

        //练习4
        double number = 80000;
        int year = 2006;
        while(number<=200000)
        {
            number *= 1.25;
            year++;
        }
        Console.WriteLine("在{0}年,学员人数达到20万人", year);
    }

8. Continue(s)

立即结束本次循环判断条件,如果成立,则进入下一次循环,否则退出循环。

就好比:1.运动员跑步喝水;2.程序员编写代码的时候突然遇到bug。

练习1:用while continue实现计算1到100(含)之间的除了能被7整除之外所有整数的和。

static void Main(string[] args)
    {
        int i = 1, sum = 0;
        while(i<=100)
        {
            if(i%7==0)
            {
                i++;
                continue;
            }
            sum += i;
            i++;
        }
        Console.WriteLine(sum);
    }

练习2:找出100以内所有的素数。

static void Main(string[] args)
    {
        int i = 2, sum = 0;
        while (i <= 100)
        {
            int j = 1;
            int count = 0;
            while (j < i)
            {
                if(i%j==0)
                {
                    count++;
                }
                j++;
            }
            if(count==1)
            {
                sum += i;
            }
            i++;
        }
        Console.WriteLine(sum);
    }

9. for循环

语法:

for(表达式1;表达式2;表达式3)

{

       循环体;

}

表达式1一般为声明循环变量,记录循环次数(int i=0;)

表达式2一般为循环条件(i<10)

表达式3一般为改变循环条件的代码,使循环条件终有一天不再成立(i++)

执行过程:程序首先执行表达式1,声明了一个循环变量用来记录循环的次数,然后执行表达式2,判断循环条件是否成立,如果表达式2返回的结果为true,则执行循环体,当执行完循环体后,执行表达式3,然后执行表达式2继续判断循环条件是否成立,如果成立则继续执行循环体,如果不成立,则跳出for循环。

练习1:求1-100之间的所有偶数和。

static void Main(string[] args)
    {
        int sum_OuShu = 0;
        for(int i=1;i<=100;i++)
        {
            if(i%2==0)
            {
                sum_OuShu += i;
            }
        }
        Console.WriteLine("1-100间的所有偶数的和为:" + sum_OuShu);
    }

练习2:找出100-999之间的水仙花数。(水仙花数:每个位置上的数字的3次幂之和等于这个数本身)

static void Main(string[] args)
    {
        for(int i=100;i<=999;i++)
        {
            if(Math.Pow(i/100,3)+Math.Pow(i/10%10,3)+Math.Pow(i%10,3)==i)
            {
                Console.WriteLine("{0}是一个水仙花数",i);
            }
        }
    }

练习3:输出九九乘法表。

static void Main(string[] args)
    {
        for(int i=1;i<=9;i++)
        {
            for(int j=1;j<=i;j++)
            {
                Console.Write("{0}x{1}={2} ", j, i, i * j);
            }
            Console.WriteLine();
        }
    }

10. 三元表达式

语法:

表达式1 ? 表达式2 : 表达式3;

表达式1一般为一个关系表达式。

如果表达式1的值为true,那么表达式2的值就是整个三元表达式的值;

如果表达式1的值为false,那么表达式3的值就是整个三元表达式的值。

注意:表达式2的结果类型必须跟表达式3的结果类型一致,并且也要跟整个三元表达式的结果类型一致。

static void Main(string[] args)
    {
        //计算两个数字的大小,求出最大的树
        Console.WriteLine("请输入第一个数字:");
        int num1 = Convert.ToInt32(Console.ReadLine());
        Console.WriteLine("请输入第二个数字:");
        int num2 = Convert.ToInt32(Console.ReadLine());
        int max = num1 > num2 ? num1 : num2;
        Console.WriteLine("最大值为:" + max);
    }

产生随机数:

static void Main(string[] args)
    {
        //产生随机数
        //1.创建能够产生随机数的对象
        Random r = new Random();
        //2.让产生随机数的这个对象调用方法来产生随机数
        int randomNum = r.Next(1, 10);
        Console.WriteLine("产生的随机数为:" + randomNum);
    }

五、复杂数据类型

1.Const常量

声明常量的语法:

const  变量类型 变量名=值;

注意:const int number = 10; //常量不能够被重新赋值

2.Enum枚举

语法:

[public] enum 枚举名

{

        值1,

        值2,

         .......

}

public:访问修饰符,公开的公共的,哪儿都可以访问;

enum:关键字,声明枚举的关键字;

枚举名:要符合Pascal命名规范。

将枚举声明到命名空间的下面,类的外面,表示这个命名空间下,所有的类都可以使用这个枚举。

枚举其实就是一个变量类型(int / double / string / decimal),

只是枚举声明、赋值、使用的方式跟那些普通的变量类型不一样。

枚举类型和 string及int类型之间的转换:

public enum QQState
{
    OnLine=2,
    OffLine,
    Leave=5,
    Busy,
    QMe
}

classProgram
{
  static void Main(string[] args)
    {
        #region 将枚举类型强转成int类型
        QQState state1 = QQState.OnLine;
        //枚举类型默认可以跟int类型互相转换,枚举类型跟int类型是兼容的
        int n1 = (int)state1;
        Console.WriteLine(n1);
        Console.WriteLine((int)QQState.OffLine);
        Console.WriteLine((int)QQState.Leave);
        Console.WriteLine((int)QQState.Busy);
        Console.WriteLine((int)QQState.QMe);
        #endregion

        #region 将int类型强转为枚举类型
        int n2 = 3;
        QQState state2 = (QQState)n2;
        Console.WriteLine(state2);
        #endregion
    }
}

注意:所有的类型都能够转换成string类型,调用ToString()即可:

public enum QQState
{
    OnLine=2,
    OffLine,
    Leave=5,
    Busy,
    QMe
}

class Progaram
{
    static void Main(string[] args)
    {
        //所有的类型都能够转换成string类型
        int n1 = 10;
        double n2 = 3.14;
        decimal n3 = 5000m;
        QQState state = QQState.OnLine;
        Console.WriteLine(n1.ToString());
        Console.WriteLine(n2.ToString());
        Console.WriteLine(n3.ToString());
        Console.WriteLine(state.ToString());
    }
}

将string类型转换成枚举类型:

public enum QQState
{
    OnLine=2,
    OffLine,
    Leave=5,
    Busy,
    QMe
}

class Progaram
{
    static void Main(string[] args)
    {
        //将string类型转换成枚举类型
        string s = "0";//Conver.ToInt32()  int.Parse()  int.TryParse()
        //调用Parse()方法的目的就是为了让它帮助我们将一个字符串转换成对应的枚举类型
        QQState state = (QQState)Enum.Parse(typeof(QQState), s);
        Console.WriteLine(state);
    }
}

总结:我们可以将一个枚举类型的变量跟int类型和string类型互相转换。

枚举类型默认是跟 int类型相互兼容的,所以可以通过强制类型转换的语法互相转换。

当转换一个枚举中没有的值的时候,不会抛异常,而是直接将数字显示出来。

枚举同样也可以跟string类型互相转换,如果将枚举类型转换成string类型,则直接调用ToString();

如果将字符串转换成枚举类型则需要下面这样一行代码:

(要转换的枚举类型)Enum.Parse(typeof(要转换的枚举类型),“要转换的字符串”);

如果转换的字符串是数字,则就算枚举中没有,也不会抛异常;

如果转换的字符串是文本,如果枚举中没有,则会抛出异常。

练习

提示用户选择一个在线状态,我们接收,并将用户的输入转换成枚举类型,再次打印到控制台上。

using System;

public enum QQState
{
    OnLine=1,
    OffLine,
    Leave,
    Busy,
    QMe
}

class Progaram
{
    static void Main(string[] args)
    {
        //练习:提示用户选择一个在线状态,我们接收,并将用户的输入转换成枚举类型,再次打印到控制台上。
        Console.WriteLine("请选择您的qq在线状态:1--OnLine  2--OffLine  3--Leave  4--Busy  5--QMe");
        string input = Console.ReadLine();
        switch(input)
        {
            case "1":
                QQState s1 = (QQState)Enum.Parse(typeof(QQState), input);
                Console.WriteLine("您选择的在线状态是{0}", s1);
                break;
            case "2":
                QQState s2 = (QQState)Enum.Parse(typeof(QQState), input);
                Console.WriteLine("您选择的在线状态是{0}", s2);
                break;
            case "3":
                QQState s3 = (QQState)Enum.Parse(typeof(QQState), input);
                Console.WriteLine("您选择的在线状态是{0}", s3);
                break;
            case "4":
                QQState s4 = (QQState)Enum.Parse(typeof(QQState), input);
                Console.WriteLine("您选择的在线状态是{0}", s4);
                break;
            case "5":
                QQState s5 = (QQState)Enum.Parse(typeof(QQState), input);
                Console.WriteLine("您选择的在线状态是{0}", s5);
                break;
        }    
    }
}

3. Struct结构

结构的作用:可以帮我们一次性声明多个不同类型的变量。

语法:

[public] struct 结构名
{

       成员;//字段

}

注意:变量在程序运行期间只能存储一个值,而字段可以存储多个值。

举例如下:

public struct Person
{
    public string _name;//字段,前面加个下划线(区别于变量)
    public int _age;
    public Gender _gender;
}

public enum Gender
{
    男,
    女
}

class Progaram
{
    static void Main(string[] args)
    {
        Person zsPerson;
        zsPerson._name = "张三";
        zsPerson._age = 21;
        zsPerson._gender = Gender.男;

        Person lsPerson;
        zsPerson._name = "李四";
        zsPerson._age = 18;
        zsPerson._gender = Gender.女;
    }
}

C#学习笔记_第8张图片

 答案如下:

public struct MyColor
{
    public int _red;
    public int _green;
    public int _blue;
}

public enum Gender
{
    boy,
    girl
}

public struct Person
{
    public string name;
    public Gender gender;
    public int age;
}

class Progaram
{
    static void Main(string[] args)
    {
        //练习1:
        MyColor mc;
        mc._red = 255;
        mc._blue = 0;
        mc._green = 0;

        //练习2:
        Person zsPerson;
        zsPerson.name = "张三";
        zsPerson.gender = Gender.boy;
        zsPerson.age = 18;
        Person xlPerson;
        xlPerson.name = "小兰";
        xlPerson.gender = Gender.girl;
        xlPerson.age = 16;
    }
}

4. Array数组

数组的作用:一次性存储多个相同类型的变量。

语法:

数组类型[]  数组名 = new 数组类型[数组长度];

如:int[] nums=new int[10];

当你写了上面这样一行代码后,就在内存中开辟了连续的10块空间,我们管每一个块称之为这个数组的元素。

如果你想要访问到数组中某一块元素,需要通过这个元素的下标或者索引去访问。

注意:数组的长度一旦固定了,就不能再被改变了。

//数组的声明方式:
        int[] nums1 = new int[10];
        int[] nums2 = { 1, 2, 3, 4, 5 };
        int[] nums3 = new int[3] { 1, 2, 3 };
        int[] nums4 = new int[] { 1, 2, 3, 4, 5 };

六、函数

七、飞行棋项目

1.画游戏头;

2.初始化地图(加载地图所需要的资源);

3.画地图;

4.玩游戏;

游戏规则:

如果玩家A踩到了玩家B,玩家B退6格;

踩到了地雷,退6格;

踩到了时空隧道,进10格;

踩到了幸运轮盘,1交换位置,2轰炸对方,使对方退6格;

踩到了暂停,暂停一回合;

踩到了方块,什么都不干;

using System;

namespace 飞行棋
{
    class Program
    {
        //用静态字段模拟全局变量
        static int[] Maps = new int[100];
        //用静态数组存储玩家A和玩家B的坐标
        static int[] PlayerPos = new int[2];
        //存储两个玩家的姓名
        static string[] PlayerNames = new string[2];
        //两个玩家的标记
        static bool[] Flags = new bool[2];

        static void Main(string[] args)
        {
            GameShow();
            #region 输入玩家姓名
            Console.ForegroundColor = ConsoleColor.White;
            Console.WriteLine("请输入玩家A的姓名:");
            PlayerNames[0] = Console.ReadLine();
            while (PlayerNames[0] == "")
            {
                Console.WriteLine("玩家A的姓名不能为空!请重新输入:");
                PlayerNames[0] = Console.ReadLine();
            }
            Console.WriteLine("请输入玩家B的姓名:");
            PlayerNames[1] = Console.ReadLine();
            while (PlayerNames[1] == ""||PlayerNames[1]==PlayerNames[0])
            {
                if (PlayerNames[1] == "")
                {
                    Console.WriteLine("玩家B的姓名不能为空!请重新输入:");
                    PlayerNames[1] = Console.ReadLine();
                }
                else
                {
                    Console.WriteLine("玩家B的姓名不能和玩家A的姓名相同!请重新输入:");
                    PlayerNames[1] = Console.ReadLine();
                }
            }
            #endregion
            //玩家姓名输入之后,首先应该清屏
            Console.Clear();//清屏
            GameShow();
            Console.WriteLine("{0}的士兵用A表示", PlayerNames[0]);
            Console.WriteLine("{0}的士兵用B表示", PlayerNames[1]);
            //在画地图前,首先应该初始化地图
            InitailMap();
            DrawMap();
            //当玩家A和玩家B都没有到达终点的时候,两个玩家会不停的玩这个游戏
            while(PlayerPos[0]<99&& PlayerPos[1]<99)
            {
                if (Flags[0] == false)
                {
                    PlayGame(0);
                }
                else
                {
                    Flags[0] = false;
                }
                if(PlayerPos[0]>=99)
                {
                    Console.WriteLine("玩家{0}无耻的赢了玩家{1}!!!", PlayerNames[0], PlayerNames[1]);
                    break;
                }
                if (Flags[1] == false)
                {
                    PlayGame(1);
                }
                else
                {
                    Flags[1] = false;
                }
                if (PlayerPos[1] >= 99)
                {
                    Console.WriteLine("玩家{0}无耻的赢了玩家{1}!!!", PlayerNames[1], PlayerNames[0]);
                    break;
                }
            }//while

            GameOver();

            Console.ReadKey();
        }

        /// 
        /// 画游戏头
        /// 
        public static void GameShow()
        {
            Console.ForegroundColor = ConsoleColor.Yellow;
            Console.WriteLine("********************************");
            Console.ForegroundColor = ConsoleColor.Red;
            Console.WriteLine("********************************");
            Console.ForegroundColor = ConsoleColor.Green;
            Console.WriteLine("********************************");
            Console.ForegroundColor = ConsoleColor.Blue ;
            Console.WriteLine("*************飞行棋*************");
            Console.ForegroundColor = ConsoleColor.Cyan;
            Console.WriteLine("********************************");
            Console.ForegroundColor = ConsoleColor.DarkMagenta;
            Console.WriteLine("********************************");
        }

        /// 
        /// 初始化地图
        /// 
        public static void InitailMap()
        {
            int[] luckyturn = { 6, 23, 40, 55, 69, 83 };//幸运轮盘●
            for(int i=0;i
        /// 画地图
        /// 
        public static void DrawMap()
        {
            Console.WriteLine("图例:幸运轮盘○,地雷☆,暂停△,时空隧道卍");
            //第一横行
            for (int i=0;i<30;i++)
            {
                Console.Write(DrawStringMap(i));
            }

            //画完第一横行后,应该换行
            Console.WriteLine();

            //第一竖列
            for(int i=30;i<35;i++)
            {
                for(int j=0;j<=28;j++)
                {
                    Console.Write("  ");
                }
                Console.Write(DrawStringMap(i));
                //换行
                Console.WriteLine();
            }

            //第二横行
            for(int i=64;i>=35;i--)
            {
                Console.Write(DrawStringMap(i));
            }

            //画完第一横行后,应该换行
            Console.WriteLine();

            //第二竖列
            for(int i=65;i<=69;i++)
            {
                Console.WriteLine(DrawStringMap(i));
            }

            //第三横行
            for(int i=70;i<=99;i++)
            {
                Console.Write(DrawStringMap(i));
            }

            //画完第三横行后,应该换行
            Console.WriteLine();
        }

        /// 
        /// 从画地图的方法中抽象出来的一个方法
        /// 
        /// 
        /// 
        public static string DrawStringMap(int i)
        {
            string str = "";
            //如果玩家A和玩家B的坐标相同,并且都在这个地图上,画一个尖括号
            if (PlayerPos[0] == PlayerPos[1] && PlayerPos[0] == i)
            {
                str="<>";
            }
            else if (PlayerPos[0] == i)
            {
                //注意:这里的A是一个全角下的A
                str = "A";
            }
            else if (PlayerPos[1] == i)
            {
                //注意:这里的B是一个全角下的B
                str = "B";
            }
            else
            {
                switch (Maps[i])
                {
                    case 0:
                        Console.ForegroundColor = ConsoleColor.Yellow;
                        str = "□";
                        break;
                    case 1:
                        Console.ForegroundColor = ConsoleColor.Green;
                        str = "●";
                        break;
                    case 2:
                        Console.ForegroundColor = ConsoleColor.Red;
                        str = "☆";
                        break;
                    case 3:
                        Console.ForegroundColor = ConsoleColor.Blue;
                        str = "△";
                        break;
                    case 4:
                        Console.ForegroundColor = ConsoleColor.DarkCyan;
                        str = "卍";
                        break;
                }//switch
            }//else
            return str;
        }

        /// 
        /// 玩游戏
        /// 
        public static void PlayGame(int playerNumber)
        {
            Random r = new Random();
            int rNumber = r.Next(1, 7);
            Console.WriteLine("{0}按任意键开始掷骰子", PlayerNames[playerNumber]);
            Console.ReadKey(true);
            Console.WriteLine("{0}掷出了{1}", PlayerNames[playerNumber],rNumber);
            PlayerPos[playerNumber] += rNumber;
            ChangePos();
            Console.ReadKey(true);
            Console.WriteLine("{0}按任意键开始行动", PlayerNames[playerNumber]);
            Console.ReadKey(true);
            Console.WriteLine("{0}行动完了", PlayerNames[playerNumber]);
            Console.ReadKey(true);
            //玩家A有可能踩到了玩家B, 方块,幸运转盘,地雷,暂停,时空隧道
            if (PlayerPos[playerNumber] == PlayerPos[1- playerNumber])
            {
                Console.WriteLine("玩家{0}踩到了玩家{1},玩家{2}退6格", PlayerNames[playerNumber], PlayerNames[1- playerNumber], PlayerNames[1- playerNumber]);
                PlayerPos[1- playerNumber] -= 6;
                ChangePos();
                Console.ReadKey(true);
            }
            else//踩到了关卡
            {
                //玩家的坐标
                switch (Maps[PlayerPos[playerNumber]])
                {
                    case 0:
                        Console.WriteLine("玩家{0}踩到了方块,安全。", PlayerNames[playerNumber]);
                        Console.ReadKey(true);
                        break;
                    case 1:
                        Console.WriteLine("玩家{0}踩到了幸运转盘,请选择 1--交换位置,2--轰炸对方。", PlayerNames[playerNumber]);
                        string input = Console.ReadLine();
                        while (true)
                        {
                            if (input == "1")
                            {
                                Console.WriteLine("玩家{0}选择和玩家{1}交换位置。", PlayerNames[playerNumber], PlayerNames[1- playerNumber]);
                                Console.ReadKey(true);
                                int temp = PlayerPos[playerNumber];
                                PlayerPos[playerNumber] = PlayerPos[1- playerNumber];
                                PlayerPos[1- playerNumber] = temp;
                                Console.WriteLine("交换完成!!!按任意键继续游戏!!!");
                                Console.ReadKey(true);
                                break;
                            }
                            else if (input == "2")
                            {
                                Console.WriteLine("玩家{0}选择轰炸玩家{1},玩家{2}退6格。", PlayerNames[playerNumber], PlayerNames[1- playerNumber], PlayerNames[1- playerNumber]);
                                Console.ReadKey(true);
                                PlayerPos[1- playerNumber] -= 6;
                                ChangePos();
                                Console.WriteLine("玩家{0}退了6格。", PlayerNames[1- playerNumber]);
                                Console.ReadKey(true);
                                break;
                            }
                            else
                            {
                                Console.WriteLine("注意:只能输入1或者2,1--交换位置,2--轰炸对方。");
                                input = Console.ReadLine();
                            }
                        }
                        Console.ReadKey(true);
                        break;
                    case 2:
                        Console.WriteLine("玩家{0}踩到了地雷,退6格。", PlayerNames[playerNumber]);
                        Console.ReadKey(true);
                        PlayerPos[playerNumber] -= 6;
                        ChangePos();
                        break;
                    case 3:
                        Console.WriteLine("玩家{0}踩到了暂停,暂停一回合。", PlayerNames[playerNumber]);
                        Flags[playerNumber] = true;
                        Console.ReadKey(true);
                        break;
                    case 4:
                        Console.WriteLine("玩家{0}踩到了时空隧道,前进10格。", PlayerNames[playerNumber]);
                        PlayerPos[playerNumber] += 10;
                        ChangePos();
                        Console.ReadKey(true);
                        break;
                }//switch
            }//else
            ChangePos();
            Console.Clear();//清屏
            DrawMap();
        }

        public static void ChangePos()
        {
            if(PlayerPos[0]<0)
            {
                PlayerPos[0] = 0;
            }
            if(PlayerPos[0]>=99)
            {
                PlayerPos[0] = 99;
            }

            if (PlayerPos[1] < 0)
            {
                PlayerPos[1] = 0;
            }
            if (PlayerPos[1] >= 99)
            {
                PlayerPos[1] = 99;
            }
        }

        public static void GameOver()
        {
            Console.WriteLine("***************游戏结束****************");
        }
    }
}

游戏截图:

C#学习笔记_第9张图片

C#学习笔记_第10张图片

八、面向对象初级

面向过程:面向的是完成这件事儿的过程,强调的是完成这件事儿的动作。

比如:把大象塞进冰箱里;

1.打开冰箱门;

2.把大象塞进去,摸一下大象的耳朵;

3.关闭冰箱门;

张三(瘦小、低矮、小屌丝)

1.张三踩着小板凳打开冰箱门;

2.张三找到李四帮忙把大象塞进冰箱里,踩着板凳摸大象耳朵;

3.张三踩着板凳关闭冰箱门;

李四(高大威猛)

1.李四自己就能打开冰箱门;

2.李四自己将大象塞进冰箱里,站着就能摸大象耳朵;

3.李四自己关闭冰箱门;

由此可见,如果我们用面向过程的思想来解决这个事情,当执行这件事的人不同的时候,我们就需要为每个不同的人量身定做解决事情的方法。

面向对象:找个对象帮你做事。

把大象塞进冰箱;

我们把冰箱作为对象:

1.冰箱门可以被打开;

2.大象可以被塞进冰箱里;

3.冰箱门可以被关闭;

张三:

张三做第1件事;

张三做第2件事;

张三做第3件事;

李四:

李四做第1件事;

李四做第2件事;

李四做第3件事;

面向对象:意在写出一个通用的代码,屏蔽差异。

面向过程:关门

张三:一脚把门揣紧了;

李四:轻轻把门关上了;

王五:胡乱的把门关上了,没关严实;

面向对象:关门

门可以被关上;

我们在代码中描述一个对象,通过描述这个对象的属性和方法;

对象必须是看得见摸得着的;

灯:属性和方法

属性:

外形:长的;

亮度:500W;

颜色:白色;

方法:

会发光;

我们把这些具有相同属性和相同方法的对象进行进一步的封装,抽象出来类这个概念。

类就是个模子,确定了对象应该具有的特征(属性)和行为(方法)。

类是对象的类型。对象是根据类创建出来的。

类就是一个盖大楼的图纸,对象就是盖出来的大楼。

类的语法:

[public] class 类名

{

       字段;

       属性;

       方法;

}

写好了一个类之后,我们需要创建这个类的对象,

那么我们管创建这个类的对象的过程 称作:类的实例化。使用关键字:new。

this:表示当前这个类的对象。

类是不占内存的,而对象是占内存的。

public class Person
    {
        public string _name;
        public int _age;
        public char _gender;

        public void CHLSS()
        {
            Console.WriteLine("我叫{0},我今年{1}岁了,我是{2}生,我可以吃喝拉撒睡。",this._name,this._age,this._gender);
        }
    }
    class Program
    {
        static void Main(string[] args)
        {
            //创建Person类的对象
            Person ZhangSan = new Person();
            ZhangSan._name = "张三";
            ZhangSan._age = 23;
            ZhangSan._gender = '男';
            ZhangSan.CHLSS();
            Console.ReadKey();
        }
    }

属性

属性的作用就是保护字段,对字段的赋值和取值进行限定。

属性的本质就是两个方法,一个叫get(),一个叫set();可读、可写;

Field——字段;Method——方法;Property——属性;

举个例子:字段是女人,属性是男人,属性存在的意义是为了保护女人不轻易被外界访问。

public class Person
    {
        private string _name;
        public string Name
        {
            //当输出属性值的时候,会执行get方法
            get { return _name; }
            //当给属性赋值的时候,首先会执行set方法
            set { _name = value; }
        }

        private int _age;
        public int Age
        {
            get { return _age; }
            set
            {
                if (value < 0 || value > 100)
                {
                    value = 0;
                }
                _age = value;
            }
        }
        private char _gender;
        public char Gender
        {
            get
            {
                if (_gender != '男' && _gender != '女')
                {
                    return _gender='男';
                }
                return _gender;
            }
            set { _gender = value; }
        }

        public void CHLSS()
        {
            Console.WriteLine("我叫{0},我今年{1}岁了,我是{2}生,我可以吃喝拉撒睡。",this.Name,this.Age,this.Gender);
        }
    }
    class Program
    {
        static void Main(string[] args)
        {
            //创建Person类的对象
            Person ZhangSan = new Person();
            ZhangSan.Name = "张三";
            ZhangSan.Age = 23;
            ZhangSan.Gender = '男';
            ZhangSan.CHLSS();
            Console.ReadKey();
        }
    }

静态和非静态的区别

1.在非静态类中,既可以有实例成员,也可以有静态成员;

2.在调用实例成员的时候,需要使用对象名.实例成员;

   在调用静态成员的时候,需要使用类名.静态成员名;

总结:

1.静态成员必须使用类名去调用,而实例成员使用对象名调用;

2.静态函数中,只能访问静态成员,不允许访问实例成员;

3.实例函数中,既可以使用静态成员,也可以使用实例成员;

4.静态类中,只允许有静态成员,不允许出现实例成员;

使用:

1.如果你想要你的类当做一个“工具类”去使用,这个时候可以考虑将类写成静态的;

2.静态类在整个项目中资源共享;

释放资源:GC(Garbage Collection垃圾回收器);

构造函数

作用:帮助我们初始化对象(给对象的每个属性依次的赋值)。

构造函数是一个特殊的方法:

1.构造函数没有返回值,连void也不能写;

2.构造函数的名称必须跟类名一样;

3.构造函数是可以有重载的;(不同个数的参数)

创建对象的时候会执行构造函数。

类当中会有一个默认的无参数的构造函数,当你写一个新的构造函数之后,不管是有参还是无参,那个默认的无参构造函数都被干掉了。

class Student
    {
        public Student(string name, int age, char gender, int chinese, int math, int english)
        {
            this.Name = name;
            this.Age = age;
            this.Gender = gender;
            this.Chinese = chinese;
            this.Math = math;
            this.English = english;
        }

        private string _name;
        public string Name
        {
            get { return _name; }
            set { _name = value; }
        }

        private int _age;
        public int Age
        {
            get { return _age; }
            set { _age = value; }
        }
        private char _gender;
        public char Gender
        {
            get { return _gender; }
            set { _gender = value; }
        }

        private int _chinese;
        public int Chinese
        {
            get { return _chinese; }
            set { _chinese = value; }
        }
        private int _math;
        public int Math
        {
            get { return _math; }
            set { _math = value; }
        }
        private int _english;
        public int English
        {
            get { return _english; }
            set { _english = value; }
        }
    }

    class Program
    {
        static void Main(string[] args)
        {
            Student zhangSan = new Student("张三", 18, '男', 100, 100, 100);
        }
    }

new关键字

new帮助我们做了三件事:

1.在内存中开辟了一块空间;

2.在开辟的空间中创建对象;

3.调用对象的构造函数进行初始化对象;

this 关键字

1.代表当前类的对象;

2.在类当中显示的调用本类的构造函数;  :this

public Student(string name, int age, char gender, int chinese, int math, int english)
        {
            this.Name = name;
            this.Age = age;
            this.Gender = gender;
            this.Chinese = chinese;
            this.Math = math;
            this.English = english;
        }
        public Student(string name, int age, char gender) : this(name,age,gender,0,0,0)
        {

        }

析构函数

//当程序结束的时候,析构函数才执行
        //帮助我们释放资源
        //如果我们希望由GC自动释放资源,就不需要写析构函数
        ~Student()
        {
            Console.WriteLine("我是析构函数");
        }

练习题:

/*写一个Ticket类,有一个距离属性(本属性只读,在构造方法中赋值)
     *不能为负数,有一个价格属性,价格属性只读,
     *并且根据距离distance计算价格price(1元/公里)
     *0-100公里 票价不打折;
     *101-200公里 票价打9.5折;
     *201-300公里 票价打9折;
     *300公里以上 票价打8折;
     */
    public class Ticket
    {
        private double _distance;
        public double Distance
        {
            get { return _distance; }
        }
        public Ticket(double distance)
        {
            if(distance<0)
            {
                distance = 0;
            }
            this._distance = distance;
        }

        private double _price;
        public double Price
        {
            get
            {
                if (_distance > 0 && _distance <= 100)
                {
                    return _distance * 1.0;
                }
                else if (_distance >= 101 && _distance < 200)
                {
                    return _distance * 0.95;
                }
                else if (_distance >= 201 && _distance <= 300)
                {
                    return _distance * 0.9;
                }
                else
                {
                    return _distance * 0.8;
                }
            }
        }

        public void ShowTicket()
        {
            Console.WriteLine("{0}公里需要{1}元", Distance, Price);
        }
    }

    class Program
    {
        static void Main(string[] args)
        {
            Ticket t = new Ticket(90);
            t.ShowTicket();
            Console.ReadKey();
        }
    }

九、面向对象继承

命名空间namespace:

用于解决类重名问题,可以看做“类的文件夹”;

在一个项目中引用另一个项目中的类:

1.添加引用(在解决方案里添加引用);

2.引用命名空间;

值类型和引用类型

区别:

1.值类型和引用类型在内存上存储的地方不一样;

2.在传递值类型和引用类型的时候,传递的方式不一样,

值类型我们称之为值传递;引用类型我们称之为引用传递;

值类型:int、double、bool、char、decimal、struct、enum;

引用类型:string、class、数组;

值类型的值存储在内存的栈中;

引用类型的值存储在内存的堆中;

字符串的不可变性

当你给一个字符串重新赋值之后,老值并没有销毁,而是重新开辟一块空间存储新值。

当程序结束后,GC扫描整个内存,如果发现有的空间没有被指向,则立即把它销毁。

我们可以将字符串看做是char类型的一个只读数组。

既然可以将string看做char类型的只读数组,所以我可以通过下标去访问字符串中的某一个元素。

string s = "abcdefg";
            //将字符串转换为char类型的数组
            char[] chs = s.ToCharArray();
            chs[0] = 'b';
            //再将字符数组转换为我们的字符串
            s = new string(chs);

字符串提供的各种方法

1.Length:获得当前字符串中字符的个数;

2.ToUpper:将字符转换成大写形式;

3.ToLower:将字符串转换成小写形式;

4.Equals(lessonTwo,StringComparison.OrdinalIgnoreCase):比较两个字符串,

5.Split():分割字符串,返回字符串类型的数组;

string s = "a1b2c3d4f5++--";
            //分割字符串Split
            char[] chs = { '1', '2', '3', '+', '-' };
            string[] str = s.Split(chs, StringSplitOptions.RemoveEmptyEntries);

6.Substring():解决字符串,在截取的时候包含要截取的位置;

7.Indexof():判断某个字符串在字符串中第一次出现的位置,如果没有则返回-1;

8.LastIndexof():判断某个字符串在字符串中最后一次出现的位置,如果没有则返回-1;

9.StartsWith():判断以。。。开始;

10.EndsWith():判断以。。。结束;

11.Replace():将字符串中某个字符串替换成一个新的字符串;

12.Contains():判断某个字符串是否包含指定的字符串;

13.Trim():去掉字符串中前后的空格;

14.TrimStart():去掉字符串中前面的空格;

15.TrimEnd():去掉字符串中结尾的空格;

16.string.IsNullOrEmpty():判断一个字符串是否为空或者为null;

17.string.Join():将数组按照指定的字符串连接,返回一个字符串。

继承

我们可能会在一些类中,写一些重复的成员,我们可以将这些重复的成员单独的封装到一个类中,作为这些类的父亲。

父类(基类):Person

子类(派生类):Student、Teacher、Driver 

首先,子类继承了父类的属性和方法,但是子类并没有继承父类的私有字段。

问题:子类有没有继承父类的构造函数?

答:子类并没有继承父类的构造函数,但是子类会默认的调用父类无参的构造函数,创建父类对象,让子类可以使用父类中的成员。

所以,如果在父类中重新写了一个有参数的构造函数之后,那个无参数的构造函数就被干掉了,子类就调用不到了,所以子类会报错。

解决办法:

1.在父类中重新写一个无参数的构造函数;

2.在子类中显示的调用父类的构造函数,使用关键字:base();

继承的特性:

1.单根性:一个子类只能有一个父类;

2.传递性:

VS中"查看类图":可看清各个类之间的关系。

object是所有类的基类。

public class Person
    {
        public Person(string name, int age, char gender)
        {
            this.Name = name;
            this.Age = age;
            this.Gender = gender;
        }

        private string _name;
        public string Name
        {
            get { return _name; }
            set { _name = value; }
        }
        private int _age;
        public int Age
        {
            get { return _age; }
            set { _age = value; }
        }
        private char _gender;
        public char Gender
        {
            get { return _gender; }
            set { _gender = value; }
        }

        public void CHLSS()
        {
            Console.WriteLine("吃喝拉撒睡");
        }
    }

    public class Student : Person
    {
        public Student(string name, int age, char gender, int id) : base(name, age, gender)
        {
            this.Id = id;
        }
        private int _id;
        public int Id
        {
            get { return _id; }
            set { _id = value; }
        }

        public void Study()
        {
            Console.WriteLine("学生学习");
        }
    }

    public class Teacher : Person
    {
        public Teacher(string name, int age, char gender, double salary) : base(name, age, gender)
        {
            this.Salary = salary;
        }

        private double _salary;
        public double Salary
        {
            get { return _salary; }
            set { _salary = value; }
        }

        public void Teach()
        {
            Console.WriteLine("老师教学");
        }
    }

    public class Driver : Person
    {
        public Driver(string name, int age, char gender, int driveTime) : base(name, age, gender)
        {
            this.DriveTime = driveTime;
        }

        private int _driveTime;
        public int DriveTime
        {
            get { return _driveTime; }
            set { _driveTime = value; }
        }

        public void Drive()
        {
            Console.WriteLine("司机开车");
        }
    }

new关键字

作用:

1.创建对象;

2.隐藏从父类那里继承过来的同名成员,隐藏的后果就是子类调用不到父类的成员。

里氏转换

1.子类可以赋值给父类;(如果有一个地方需要一个父类作为参数,我们可以给一个子类代替)

2.如果父类中装的是子类对象,那么可以将这个父类强转为子类对象。

子类对象可以调用父类中的成员,但是父类对象永远都只能调用自己的成员。

is:表示类型转换;如果能够转换成功,则返回一个true,否则返回一个false;

as:表示类型转换;如果能够转换成功,则返回对应的对象,否则返回一个null;

public class Person
    {
        public void PersonSayHello()
        {
            Console.WriteLine("我是父类");
        }
    }

    public class Student : Person
    {
        public void StudentSayHello()
        {
            Console.WriteLine("我是学生");
        }
    }
    public class Teacher : Person
    {
        public void TeacherSayHello()
        {
            Console.WriteLine("我是老师");
        }
    }

    class Program
    {
        static void Main(string[] args)
        {
            Person p = new Student();
            //Student s = (Student)p;

            //is的用法v
            //if(p is Teacher)
            //{
            //    Teacher t = (Teacher)p;
            //    t.TeacherSayHello();
            //}
            //else
            //{
            //    Console.WriteLine("转换失败");
            //}
            //as的用法
            Student s = p as Student;
            s.StudentSayHello();

            Console.ReadKey();
        }
    }

注意:我们将一个对象输出到控制台,默认情况下,打印的就是这个对象所在的类的命名空间

ArrayList集合

class Program
    {
        static void Main(string[] args)
        {
            //创建一个集合对象
            ArrayList list = new ArrayList();
            //集合:很多数据的一个集合
            //数组:长度不可变、类型单一
            //集合的好处:长度可以任意改变,类型随便
            //添加单个元素如下:
            list.Add(1);
            list.Add(3.14);
            list.Add(true);
            list.Add("张三");
            list.Add('男');
            list.Add(5000m);
            list.Add(new int[] { 1, 2, 3, 4 });
            Person p = new Person();
            list.Add(p);
            list.Add(list);
            //添加集合元素如下:
            list.AddRange(new int[] { 1, 2, 3, 4, 5 });
            list.AddRange(list);
            list.Clear();//清空所有元素
            list.Remove(true);//删除单个元素 写谁就删谁
            list.RemoveAt(0);//根据下标去删除元素
            list.RemoveRange(0, 3);//根据下标去移除一定范围的元素
            list.Sort();//升序排列
            list.Reverse();//反转
            list.Insert(1, "插入的元素");//在指定的位置插入一个元素
            list.InsertRange(0, new string[] { "张三", "李四" });//在指定的位置插入一个集合
            list.Contains(1);//集合中是否包含该元素

            for (int i = 0; i < list.Count; i++)
            {
                if (list[i] is Person)
                {
                    ((Person)list[i]).SayHello();
                }
                else if(list[i] is int[])
                {
                    for(int j=0;j<((int[])list[i]).Length;i++)
                    {
                        Console.WriteLine(((int[])list[i])[j]);
                    }
                }
                else
                {
                    Console.WriteLine(list[i]);
                }
            }
            
            Console.ReadKey();
        }
    }
    public class Person
    {
        public void SayHello()
        {
            Console.WriteLine("我是人类");
        }
    }

ArrayList集合长度问题

每次集合中实际包含的元素个数(count)超过了可以包含的元素个数(capcity)的时候,集合就会向内存中申请多开辟一倍的空间,来保证集合的长度一直够用。

class Program
    {
        static void Main(string[] args)
        {
            ArrayList list = new ArrayList();
            list.Add(1);
            Console.WriteLine(list.Count);
            Console.WriteLine(list.Capacity);
            //count 表示这个集合中实际包含的元素的个数
            //capcity 表示这个集合中可以包含的元素个数
            Console.ReadKey();
        }
    }

练习:

创建一个集合,里面添加一些数字,求平均值与和。

static void Main(string[] args)
        {
            ArrayList list = new ArrayList();
            list.AddRange(new int[] { 1, 2, 3, 4, 5 });
            int sum = 0;
            for(int i=0;i

写一个长度为10的集合,要求在里面随机地存放10个数字(0-9),但是要求所有的数字不重复。

class Program
    {
        static void Main(string[] args)
        {
            ArrayList list = new ArrayList();
            Random r = new Random();
            for(int i=0;i<10;i++)
            {
                int rNumber = r.Next(0, 10);
                //集合中没有这个随机数
                if(!list.Contains(rNumber))
                {
                    list.Add(rNumber);
                }
                else//集合中有这个随机数
                {
                    i--;//一旦产生了重复的随机数,这次循环就不算数
                }
            }
            for(int i=0;i

HashTable 键值对集合

在键值对集合中,我们是根据键去找值的;

键值对对象[键]=值;

键值对集合当中,键必须是唯一的,而值是可以重复的。

foreach循环:用来遍历键值对。

var:根据值能够推断出来类型

C#是一门强类型语言:在代码当中,必须对每一个变量的类型有一个明确的定义;

如:int n=15;

js是一门弱语言,如:12  3.14  true  "fdafda"  'c'  var

class Program
    {
        static void Main(string[] args)
        {
            //创建了一个键值对集合对象
            Hashtable ht = new Hashtable();
            ht.Add(1, "张三");
            ht.Add(2, true);
            ht.Add(3, '男');
            ht.Add(false, "错误");
            ht.Add(5,"张三");
            ht[6]="李四";//这也是一种添加数据的方式
            ht[1]="把张三覆盖掉";
            //在键值对集合中,是根据键去找值的
            //Console.WriteLine(ht[1]);
            //Console.WriteLine(ht[2]);
            //Console.WriteLine(ht[3]);
            //Console.WriteLine(ht[false]);
            foreach(var item in ht.Keys)
            {
                Console.WriteLine(ht[item]);
            }
            Console.ReadKey();
        }
    }

Path

static void Main(string[] args)
        {
            string str = @"C:\3000soft\Red Spider\Data\Message\老赵.wav";
            //获得文件名
            Console.WriteLine(Path.GetFileName(str));
            //获得文件名但是不包括扩展名
            Console.WriteLine(Path.GetFileNameWithoutExtension(str));
            //获得文件的扩展名
            Console.WriteLine(Path.GetExtension(str));
            //获得文件所在的文件夹的名称
            Console.WriteLine(Path.GetDirectoryName(str));
            //获得文件所在的全路径
            Console.WriteLine(Path.GetFullPath(str));
            //连接两个字符串作为路径
            Console.WriteLine(Path.Combine(@"C:\a\", "b.txt"));
            Console.ReadKey();
        }

File

static void Main(string[] args)
        {
            //创建一个文件
            //File.Create(@"G:\测试_备用\file学习.txt");
            //删除一个文件
            //File.Delete(@"G:\测试_备用\file学习.txt");
            //复制一个文件
            //File.Copy(@"G:\测试_备用\复制.txt", @"G:\测试_备用\粘贴.txt");
            //读取文件
            //byte[] buffer = File.ReadAllBytes(@"G:\测试_备用\file学习.txt");
            //string s = Encoding.Default.GetString(buffer);
            //写入文件(没有这个文件的话,会给你创建一个,有的话,会给你覆盖掉)
            //string str = "今天天气好晴朗,处处好风光";
            //byte[] buffer = Encoding.Default.GetBytes(str);
            //File.WriteAllBytes(@"G:\测试_备用\file学习.txt", buffer);
            //逐行读取(返回值为数组)
            string[] contents = File.ReadAllLines(@"G:\测试_备用\file学习.txt", Encoding.Default);
            foreach(string item in contents)
            {
                Console.WriteLine(item);
            }
            //逐行写入
            File.WriteAllLines(@"G:\测试_备用\file学习.txt", new string[] { "abc", "def" });
            //读取文本(返回值为字符串)
            string str = File.ReadAllText(@"G:\测试_备用\file学习.txt", Encoding.Default);
            Console.WriteLine(str);
            Console.ReadKey();
            //写入文本
            File.WriteAllText(@"G:\测试_备用\file学习.txt", "abcdef");
            File.AppendAllText(@"G:\测试_备用\file学习.txt", "增加字符串,不会覆盖");
        }

编码:

将字符串以怎样的形式保存为二进制。

乱码

产生乱码的原因:就是你保存这个文件采用的编码,跟你打开这个文件所采用的编码格式不一样。

绝对路径和相对路径

绝对路径:通过给定的这个路径直接能在我的电脑中找到这个文件;

相对路径:文件相对于应用程序的路径;

我们在开发中应该去尽量的使用相对路径。

泛型集合

static void Main(string[] args)
        {
            //创建泛型集合对象
            List list = new List();
            list.Add(1);
            list.Add(2);
            list.Add(3);
            list.AddRange(new int[] { 1, 2, 3, 4, 5, 6 });
            list.AddRange(list);

            //List泛型集合可以转换为数组
            int[] nums= list.ToArray();
            List listTwo= nums.ToList();
        }

装箱和拆箱

装箱:将值类型转换为引用类型;

拆箱:将引用类型转换为值类型;

看两种类型是否发生了装箱和拆箱,要看,这两种类型是否存在继承关系。

static void Main(string[] args)
        {
            int n = 10;
            object o = n;//装箱
            int n2 = (int)o;//拆箱

            //ArrayList list = new ArrayList(); //用时00:00:06.3582923
            List list = new List();//用时00:00:00.5262316
            //这个循环使用ArrayList发生了10000万次装箱操作
            Stopwatch sw = new Stopwatch();
            sw.Start();
            for(int i=0;i<100000000;i++)
            {
                list.Add(i);
            }
            sw.Stop();
            Console.WriteLine(sw.Elapsed);
            Console.ReadKey();

            //这个地方没有发生任何的装箱和拆箱
            string str = "123";
            int i = Convert.ToInt32(str);

            int a = 10;
            IComparable b = a;//装箱
        }

Dictionary字典

static void Main(string[] args)
        {
            Dictionary dic = new Dictionary();
            dic.Add(1, "张三");
            dic.Add(2, "李四");
            dic.Add(3, "王五");
            dic[1] = "让张三滚蛋";
            foreach(var item in dic.Keys)
            {
                Console.WriteLine("{0}----{1}", item, dic[item]);
            }
            foreach(KeyValuePair kv in dic)
            {
                Console.WriteLine("{0}----{1}", kv.Key, kv.Value);
            }
            Console.ReadKey();
        }

练习

/*将一个数组中的奇数放到一个集合中,再将偶数放到另一个集合中
             * 最终将两个集合合并为一个集合,并且奇数显示在左边,偶数显示在右边。
             */
            int[] nums = { 1, 2, 3, 4, 5, 6, 7, 8, 9 };
            List listOu = new List();
            List listJi = new List();
            for(int i=0;i
/*提示用户输入一个字符串,通过foreach循环将用户输入的字符串赋值给一个字符数组。
             */
            Console.WriteLine("请输入一个字符串:");
            string input = Console.ReadLine();
            char[] chs = new char[input.Length];
            int i = 0;
            foreach(var item in input)
            {
                chs[i] = item;
                i++;
            }
            foreach(var item in chs)
            {
                Console.WriteLine(item);
            }
/*统计Welcome to china中每个字符出现的次数,不考虑大小写。
             */
            string str = "Welcome to China";
            //字符------出现次数
            //键--------值
            Dictionary dic = new Dictionary();
            for(int i=0;i kv in dic)
            {
                Console.WriteLine("字母{0}出现了{1}次", kv.Key, kv.Value);
            }

FileStream文件流

将创建文件流对象的过程写在using当中,会自动地帮助我们释放流所占用的资源

class Program
    {
        static void Main(string[] args)
        {
            //FileStream 用来操作字节的
            //StreamReader和StreamWriter 用来操作字符的

            /*使用FileStream来读取数据*/
            //1.创建FileStream对象
            FileStream fsRead = new FileStream(@"G:\测试_备用\new.txt", FileMode.OpenOrCreate, FileAccess.Read);
            byte[] buffer1 = new byte[1024 * 1024 * 5];
            fsRead.Read(buffer1, 0, buffer1.Length);
            //返回本次实际读取到的有效字节数
            int r = fsRead.Read(buffer1, 0, buffer1.Length);
            //将字节数组中每一个元素按照指定的编码格式解码成字符串
            string s = Encoding.Default.GetString(buffer1, 0, r);
            //关闭流
            fsRead.Close();
            //释放流所占用的资源
            fsRead.Dispose();
            Console.WriteLine(s);

            //使用FileStream来写入数据
            using (FileStream fsWrite = new FileStream(@"G:\测试_备用\new.txt", FileMode.OpenOrCreate, FileAccess.Write))
            {
                string str = "看我把你覆盖掉";
                byte[] buffer2 = Encoding.UTF8.GetBytes(str);
                fsWrite.Write(buffer2, 0, buffer2.Length);
            }

                Console.ReadKey();
        }
    }

使用文件流来实现多媒体文件的复制

class Program
    {
        static void Main(string[] args)
        {
            //思路:就是先将要复制的多媒体文件读取出来,然后再写入到你指定的位置。
            string source = @"G:\测试_备用\复制.mp3";
            string target = @"G:\测试_备用\粘贴.mp3";

            CopyFile(source, target);
            Console.ReadKey();
        }

        public static void CopyFile(string source, string target)
        {
            //我们创建一个负责读取的流
            using (FileStream fsRead = new FileStream(source, FileMode.Open, FileAccess.Read))
            {
                //创建一个负责写入的流
                using (FileStream fsWrite = new FileStream(target, FileMode.OpenOrCreate, FileAccess.Write))
                {
                    byte[] buffer = new byte[1024 * 1024 * 5];
                    //因为文件可能会比较大,所以我们在读取的时候,应该通过一个循环去读取
                    while(true)
                    {
                        //返回本次读取实际读取到的字节数
                        int r = fsRead.Read(buffer, 0, buffer.Length);
                        //如果返回一个0,也就意味着什么都没有读取到,读取完了
                        if(r==0)
                        {
                            break;
                        }
                        fsWrite.Write(buffer, 0, r);
                    }
                }
            }
        }
    }

StreamReader和SteamWriter

static void Main(string[] args)
        {
            //使用StreamReader来读取一个文本文件
            using (StreamReader sr = new StreamReader(@"G:\测试_备用\new.txt", Encoding.Default))
            {
                while (!sr.EndOfStream)
                {
                    Console.WriteLine(sr.ReadLine());
                }
            }

            //使用StreamWriter来写入一个文本文件
            using (StreamWriter sw = new StreamWriter(@"G:\测试_备用\new.txt"))
            {
                sw.Write("今天天气好晴朗");
            }
            Console.ReadKey();
        }

十、面向对象多态

概念:让一个对象能够表现出多种的状态(类型)

实现多态的三种方法:

1.虚方法;

将父类的方法标记为虚方法,使用关键字virtual,这个函数可以被子类重新写一个遍;

class Program
    {
        static void Main(string[] args)
        {
            Chinese cn1 = new Chinese("李雷");
            Chinese cn2 = new Chinese("韩梅梅");
            Japanese j1 = new Japanese("本田");
            Japanese j2 = new Japanese("丰田");
            Korea k1 = new Korea("三星");
            Korea k2 = new Korea("泡菜");
            Person[] pers = { cn1, cn2, j1, j2, k1, k2 };
            for(int i=0;i
class Program
    {
        static void Main(string[] args)
        {
            //真的鸭子嘎嘎叫,木头鸭子吱吱叫,橡皮鸭子叽叽叫
            RealDuck rd = new RealDuck();
            MTDuck md = new MTDuck();
            XPDuck xd = new XPDuck();
            RealDuck[] ducks = { rd, md, xd };
            for(int i=0;i

2.抽象类;

(1)抽象成员必须标记为abstract,并且不能有任何实现;

(2)抽象成员必须在抽象类中;

(3)抽象类不能被实例化;

(4)子类继承抽象类后,必须把父类中的所有抽象成员都重写;(除非子类也是一个抽象类,才可以不重写)

(5)抽象成员的访问修饰符不能是private;

(6)在抽象类中可以包含实例成员;并且抽象类的实例成员可以不被子类实现;
(7)抽象类是有构造函数的,虽然不能被实例化;
(8)如果父类的抽象方法中有参数,那么,继承这个抽象父类的子类在重写父类的方法的时候必须传入对应的参数;
(9)如果父类的抽象方法中有返回值,那么子类在重写这个抽象方法的时候,也必须要传入返回值;
(10)如果父类中的方法有默认的实现,并且父类需要被实例化,这时可以考虑将父类定义成一个普通类,用虚方法来实现多态;
(11)如果父类中的方法没有默认实现,父类也不需要被实例化,则可以将该类定义为抽象类。

class Program
    {
        static void Main(string[] args)
        {
            //狗会叫,猫也会叫
            Animal d = new Dog();
            d.Bark();
            Animal c = new Cat();
            c.Bark();
            Console.ReadKey();
        }
    }

    public abstract class Animal
    { 
        public abstract void Bark();
    }

    public class Dog : Animal
    {
        public override void Bark()
        {
            Console.WriteLine("旺旺");
        }
    }
    public class Cat : Animal
    {
        public override void Bark()
        {
            Console.WriteLine("喵喵");
        }
    }

练习:

using System;

namespace Test_CSharp
{
    class Program
    {
        static void Main(string[] args)
        {
            //使用多态求矩形的面积和周长以及圆形的面积和周长
            Shape shape = new Square(5, 6); //new Circle(5);
            double area = shape.GetArea();
            double perimeter = shape.GetPerimeter();
            Console.WriteLine("这个形状的面积为:{0},周长是:{1}", area, perimeter);
            Console.ReadKey();
        }
    }

    public abstract class Shape
    {
        public abstract double GetArea();
        public abstract double GetPerimeter();
    }

    public class Circle:Shape
    {
        private double _r;
        public double R
        {
            get { return _r; }
            set { _r = value; }
        }
        public Circle(double r)
        {
            this.R = r;
        }

        public override double GetArea()
        {
            return Math.PI * this.R * this.R;
        }

        public override double GetPerimeter()
        {
            return 2 * Math.PI * this.R;
        }
    }

    public class Square:Shape
    {
        private double _length;
        public double Length
        {
            get { return _length; }
            set { _length = value; }
        }
        private double _width;
        public double Width
        {
            get { return _length; }
            set { _length = value; }
        }
        public Square(double length,double width)
        {
            this.Length = length;
            this.Width = width;
        }

        public override double GetArea()
        {
            return this.Length * this.Width;
        }

        public override double GetPerimeter()
        {
            return 2 * (this.Length + this.Width);
        }
    }
}
using System;

namespace Test_CSharp
{
    class Program
    {
        static void Main(string[] args)
        {
            //用多态来实现将移动硬盘或者U盘或者MP3插到电脑上进行读写数据
            MobileDisk md = new MobileDisk();
            UDisk u = new UDisk();
            MP3Disk mp3 = new MP3Disk();
            Computer cpu = new Computer();
            cpu.CpuRead(mp3);
            cpu.CpuWrite(mp3);
            Console.ReadKey();
        }
    }

    /// 
    /// 抽象父类
    /// 
    public abstract class MobileStorage
    {
        public abstract void Read();
        public abstract void Write();
    }

    /// 
    /// 移动硬盘类
    /// 
    public class MobileDisk : MobileStorage
    {
        public override void Read()
        {
            Console.WriteLine("移动硬盘在读取数据");
        }
        public override void Write()
        {
            Console.WriteLine("移动硬盘在写入数据");
        }
    }
    /// 
    /// U盘类
    /// 
    public class UDisk : MobileStorage
    {
        public override void Read()
        {
            Console.WriteLine("U盘在读取数据");
        }
        public override void Write()
        {
            Console.WriteLine("U盘在写入数据");
        }
    }/// 
     /// MP3类
     /// 
    public class MP3Disk : MobileStorage
    {
        public override void Read()
        {
            Console.WriteLine("MP3在读取数据");
        }
        public override void Write()
        {
            Console.WriteLine("MP3在写入数据");
        }
        public void PlayMusic()
        {
            Console.WriteLine("MP3在播放音乐");
        }
    }

    public class Computer
    {
        public void CpuRead(MobileStorage ms)
        {
            ms.Read();
        }
        public void CpuWrite(MobileStorage ms)
        {
            ms.Write();
        }
    }
}

3.接口;

访问修饰符

1.public :公开的公共的
2.private:私有的,只能在当前类的内部访问
3.protected:受保护的,只能在当前类的内部以及该类的子类中访问。
4.internal:只能在当前项目中访问。在同一个项目中,internal和public的权限是一样。
5.protected internal:protected+internal

注意:

1.能够修饰类的访问修饰符只有两个:public、internal。
2.可访问性不一致。
3.子类的访问权限不能高于父类的访问权限,会暴漏父类的成员。

设计模式
概念:设计这个项目的一种方式。

简单工厂模式

using System;

namespace Test_CSharp
{
    public abstract class NoteBook
    {
        public abstract void SayHello();
    }

    public class Lenovo : NoteBook
    {
        public override void SayHello()
        {
            Console.WriteLine("我是联想笔记本");
        }
    }

    public class Acer : NoteBook
    {
        public override void SayHello()
        {
            Console.WriteLine("我是宏基笔记本");
        }
    }
    public class Dell : NoteBook
    {
        public override void SayHello()
        {
            Console.WriteLine("我是戴尔笔记本");
        }
    }
    public class IBM : NoteBook
    {
        public override void SayHello()
        {
            Console.WriteLine("我是IBM笔记本");
        }
    }


    class Program
    {
        //简单工厂的核心:根据用户的输入创建对象赋值给父类
        public static NoteBook GetNoteBook(string brand)
        {
            NoteBook nb = null;
            switch (brand)
            {
                case "Lenovo":
                    nb = new Lenovo();
                    break;
                case "Acer":
                    nb = new Acer();
                    break;
                case "Dell":
                    nb = new Dell();
                    break;
                case "IBM":
                    nb = new IBM();
                    break;
            }
            return nb;
        }
        static void Main(string[] args)
        {
            Console.WriteLine("请输入您想要的笔记本品牌:");
            string brand = Console.ReadLine();
            NoteBook nb = GetNoteBook(brand);
            nb.SayHello();
            Console.ReadKey();
        }
    }
}

序列化:就是将对象转换为二进制
反序列化:就是将二进制转换为对象
作用:传输数据。将这个类标记为可以被序列化的。

using System;
using System.IO;
using System.Runtime.Serialization.Formatters.Binary;

namespace Test_CSharp
{
    [Serializable]
    public class Person
    {
        private string _name;

        public string Name
        {
            get { return _name; }
            set { _name = value; }
        }


        private char _gender;

        public char Gender
        {
            get { return _gender; }
            set { _gender = value; }
        }

        private int _age;

        public int Age
        {
            get { return _age; }
            set { _age = value; }
        }
    }

    class Program
    {
        static void Main(string[] args)
        {
            //要将p这个对象 序列化为二进制 传输给对方电脑
            //Person p = new Person();
            //p.Name = "张三";
            //p.Age = 19;
            //p.Gender = '男';
            //using (FileStream fsWrite=new FileStream(@"G:\测试_备用\new.txt",FileMode.OpenOrCreate,FileAccess.Write))
            //{
            //    //开始序列化对象
            //    BinaryFormatter bf = new BinaryFormatter();
            //    bf.Serialize(fsWrite, p);
            //}
            //Console.WriteLine("序列化成功");

            //接手对方发送过来的二进制 反序列化成对象
            Person p;
            using (FileStream fsRead=new FileStream(@"G:\测试_备用\new.txt", FileMode.OpenOrCreate, FileAccess.Read))
            {
                BinaryFormatter bf = new BinaryFormatter();
                p = (Person)bf.Deserialize(fsRead);
            }
            Console.WriteLine(p.Name);
            Console.WriteLine(p.Age);
            Console.WriteLine(p.Gender);
            Console.ReadKey();
        }
    }
}

partial部分类

namespace Test_CSharp
{
    public partial class Person
    {
        private string _name;
        public void Test()
        { }
    }

    public partial class Person
    {
        public void Test(string name)
        { }
    }

    class Program
    {
        static void Main(string[] args)
        {
        }
    }
}

sealed密封类
不能够被其他类继承,但是可以继承于其他类。

public sealed class Person:Test
    { 
        
    }

    public class Test
    { 
        
    }

重写ToString()方法

using System;

namespace Test_CSharp
{
    class Program
    {
        static void Main(string[] args)
        {
            Person p = new Person();
            Console.WriteLine(p.ToString());
            Console.ReadKey();
        }
    }

    public class Person
    {
        public override string ToString()
        {
            return "Hello World";
        }
    }
}

接口

接口是一种规范。

只要一个类继承了一个接口,这个类就必须实现这个接口中所有的成员。

接口不能被实例化,也就是说,接口不能new(不能创建对象)。

接口中的成员不能加“访问修饰符”,接口中的成员访问修饰符为public,不能修改。

(默认为public)
接口中的成员不能有任何实现(“光说不做”,只是定义了一组未实现的成员)。

接口中只能有方法、属性、索引器、事件,不能有“字段”和构造函数。

接口与接口之间可以继承,并且可以多继承。

接口并不能去继承一个类,而类可以继承接口  (接口只能继承于接口,而类既可以继承接口,也可以继承类)

实现接口的子类必须实现该接口的全部成员。

一个类可以同时继承一个类并实现多个接口,如果一个子类同时继承了父类A,并实现了接口IA,那么语法上A必须写在IA的前面。

class MyClass:A,IA{},因为类是单继承的。

显式实现接口的目的:解决方法的重名问题。
什么时候显式的去实现接口:当继承的接口中的方法和参数一摸一样的时候,要是用显式的实现接口。

当一个抽象类实现接口的时候,需要子类去实现接口。

using System;

namespace Test_CSharp
{
    public interface IKouLanable
    {
        void KouLan();
    }

    public class Person
    {
        public void CHLSS()
        {
            Console.WriteLine("吃喝拉撒睡");
        }
    }

    public class NBAPlayer
    {
        public void KouLan()
        {
            Console.WriteLine("我会扣篮");
        }
    }

    public class Student : Person, IKouLanable
    {
        public void KouLan()
        {
            Console.WriteLine("我也会扣篮");
        }
    }

    class Program
    {
        static void Main(string[] args)
        {
            //接口就是一个规范、能力。
        }
    }
}

接口的语法特征:

public interface IFlyable
    {
        //接口中的成员不允许添加访问修饰符 ,默认就是public
        void Fly();
        string Test();
        //不允许写具有方法体的函数

        //string _name;
         string Name
        {
            get;
            set;
        }
    }

自动属性和普通属性

public class Person
    {
        private string _name;

        public string Name
        {
            get { return _name; }
            set { _name = value; }
        }

        /// 
        /// 自动属性
        /// 
        public int Age
        {
            get;
            set;
        }
    }

接口特点:

using System;

namespace Test_CSharp
{
    public interface IFlyable
    {
        //不允许有访问修饰符,默认为public
        //方法、自动属性
        void Fly();
    }

    public class Bird : IFlyable
    {
        public void Fly()
        {
            Console.WriteLine("鸟在飞");
        }
    }

    public class Person : IFlyable
    {
        public void Fly()
        {
            Console.WriteLine("人在飞");
        }
    }

    public class Student
    {
        public void Fly()
        {
            Console.WriteLine("人在飞");
        }
    }

    class Program
    {
        static void Main(string[] args)
        {
            IFlyable fly = new Bird();//new Person();
            fly.Fly();
            Console.ReadKey();
        }
    }

    public interface M1
    {
        void Test1();
    }

    public interface M2
    {
        void Test2();
    }

    public interface M3
    {
        void Test3();
    }


    public interface SupperInterface : M1, M2, M3
    {

    }

    public class Car : SupperInterface
    {

        public void Test1()
        {
            throw new NotImplementedException();
        }

        public void Test2()
        {
            throw new NotImplementedException();
        }

        public void Test3()
        {
            throw new NotImplementedException();
        }
    }
}

接口练习:

using System;

namespace Test_CSharp
{
    //麻雀会飞  鹦鹉会飞  鸵鸟不会飞 企鹅不会飞  直升飞机会飞
    //用多态来实现
    //需方法、抽象类、接口
    public interface IFlyable
    {
        void Fly();
    }

    public interface ISpeak
    {
        void Speak();
    }

    public class Bird
    {
        public double Wings
        {
            get;
            set;
        }
        public void EatAndDrink()
        {
            Console.WriteLine("会吃会喝");
        }
    }

    public class MaQue : Bird, IFlyable
    {
        public void Fly()
        {
            Console.WriteLine("麻雀会飞");
        }
    }

    public class YingWu : Bird, IFlyable, ISpeak
    {
        public void Fly()
        {
            Console.WriteLine("鹦鹉会飞");
        }
        public void Speak()
        {
            Console.WriteLine("鹦鹉可以学习人类说话");
        }
    }

    public class TuoBird : Bird
    {

    }

    public class QQ : Bird
    {

    }

    public class Plane : IFlyable
    {
        public void Fly()
        {
            Console.WriteLine("直升飞机转动螺旋桨飞行");
        }
    }

    public 


    class Program
    {
        static void Main(string[] args)
        {
            IFlyable fly = new Plane();//new MaQue();//new YingWu();
            fly.Fly();
            Console.ReadKey();
        }
    }
}

显式实现接口:

using System;

namespace Test_CSharp
{
    class Program
    {
        static void Main(string[] args)
        {
            //显式实现接口就是为了解决方法的重名问题
            IFlyable fly = new Bird();
            fly.Fly();
            Bird bird = new Bird();
            bird.Fly();

            Console.ReadKey();
        }
    }


    public class Bird : IFlyable
    {
        public void Fly()
        {
            Console.WriteLine("鸟飞会");
        }
        /// 
        /// 显式实现接口
        /// 
        void IFlyable.Fly()
        {
            Console.WriteLine("我是接口的飞");
        }

    }

    public interface IFlyable
    {
        void Fly();
    }
}

超市收银系统:

using System;
using System.Collections.Generic;

namespace 超市收银系统
{
    //商品的父类(三个自动属性:ID、价格、名称)
    class ProductFather
    {
        /*自动属性:
         * 1.自动实现的属性必须同时声明 get和 set 访问器。创建 readonly自动实现属性时,需要将set访问器设置为private 。
         * 2.自动实现的属性上可以使用特性,不能用在支持后备字段上。如果属性的后备字段上使用特性,则应该只创建常规属性。
         * 3.自动实现属性get,和set中不能包含特殊的逻辑处理。与字段类似,但不同于字段。与字段不同,属性不作为变量来分类,不能将属性作为 ref参数或 out参数传递。
         */
        public string ID
        {
            get;
            set;
        }

        public double Price
        {
            get;
            set;
        }

        public string Name
        {
            get;
            set;
        }

        //构造函数:帮助我们初始化对象(给对象的每个属性依次的赋值)。
        public ProductFather(string id, double price, string Name)
        {
            this.ID = id;
            this.Price = price;
            this.Name = Name;
        }
    }

    //宏基电脑
    class Acer : ProductFather
    {
        public Acer(string id, double price, string name)
            : base(id, price, name)
        {

        }
    }

    //三星手机
    class SamSung : ProductFather
    {
        public SamSung(string id, double price, string name)
            : base(id, price, name)
        {

        }
    }

    //酱油
    class JiangYou : ProductFather
    {
        public JiangYou(string id, double price, string name)
            : base(id, price, name)
        {

        }
    }

    //香蕉
    class Banana : ProductFather
    {
        public Banana(string id, double price, string name)
            : base(id, price, name)
        {

        }
    }

    //打折的父类(抽象类:父类中的方法没有默认实现,父类也不需要被实例化)
    abstract class CalFather
    {
        /// 
        /// 计算打折后应付多少钱
        /// 
        /// 打折前应付的钱
        /// 打折后应付的钱
        public abstract double GetTotalMoney(double realMoney);
    }

    //不打折,该多少钱就多少钱
    class CalNormal : CalFather
    {
        public override double GetTotalMoney(double realMoney)
        {
            return realMoney;
        }
    }

    //满减优惠:满M元,减N元
    class CalMN : CalFather
    {
        //自动属性
        public double M
        {
            get;
            set;
        }

        public double N
        {
            get;
            set;
        }

        public CalMN(double m, double n)
        {
            this.M = m;
            this.N = n;
        }

        public override double GetTotalMoney(double realMoney)
        {
            if (realMoney >= this.M)
            {
                return realMoney - (int)(realMoney / this.M) * this.N;
            }
            else
            {
                return realMoney;
            }
        }
    }

    //按折扣率打折
    class CalRate : CalFather
    {
        //折扣率
        public double Rate
        {
            get;
            set;
        }

        public CalRate(double rate)
        {
            this.Rate = rate;
        }

        public override double GetTotalMoney(double realMoney)
        {
            return realMoney * this.Rate;
        }
    }

    //仓库
    class CangKu
    {
        //集合
        List> list = new List>();

        /*list[0]存储Acer电脑
         *list[1]存储三星手机
         *list[2]存储酱油
         *list[3]存储香蕉 */

        //在创建仓库对象的时候,向仓库中添加4层货架
        public CangKu()
        {
            list.Add(new List());//list[0] 第一层货架(父类:商品)
            list.Add(new List());//list[1] 第二层货架(父类:商品)
            list.Add(new List());//list[2] 第三层货架(父类:商品)
            list.Add(new List());//list[3] 第四层货架(父类:商品)
        }

        //进货(两个参数:商品类型,商品数量)
        public void JinPros(string strType,int count)
        {
            for(int i=0;i

GUID

产生一个不会重复的编号。

class Program
    {
        static void Main(string[] args)
        {
            //产生一个不会重复的编号
            Console.WriteLine(Guid.NewGuid().ToString());
            Console.WriteLine(Guid.NewGuid().ToString());
            Console.WriteLine(Guid.NewGuid().ToString());
            Console.WriteLine(Guid.NewGuid().ToString());
            Console.WriteLine(Guid.NewGuid().ToString());
            Console.ReadKey();
        }
    }

十一、Winform基础

1. 概念

Winform应用程序是一种智能客户端技术,我们可以使用winform应用程序帮助我们获得信息或者传输信息等。

2. 属性

Name:在后台要获得前台的控件对象,需要使用Name属性;

Visible:指示一个控件是否可见;

Enabled:指示一个控件是否可用。

3. 事件:发生一件事情。

注册事件:双击控件注册的都是控件默认被选中的那个事件。

触发事件;

4. 在Main函数当中创建的窗体对象,我们称之为这个窗体应用程序的主窗体,

也就意味着,当你将主窗体关闭后,整个应用随之关闭。

例子: 点击“爱”和“不爱”小游戏。

using System;
using System.Drawing;
using System.Windows.Forms;

namespace WindowsFormsApp1
{
    public partial class Form1 : Form
    {
        public Form1()
        {
            InitializeComponent();
        }

        /// 
        /// 当鼠标进入按钮可见部分的时候,给按钮一个新的坐标
        /// 
        /// 
        /// 
        private void btn_unlove_MouseEnter(object sender, EventArgs e)
        {
            //给按钮一个新的坐标
            //这个按钮活动的最大宽度就是 窗体的宽度减去按钮的宽度
            int x = this.ClientSize.Width - btn_unlove.Width;
            int y = this.ClientSize.Height - btn_unlove.Height;

            Random r = new Random();
            //要给按钮一个随机的坐标
            btn_unlove.Location = new Point(r.Next(0, x + 1), r.Next(0, y + 1));
        }

        private void btn_love_Click(object sender, EventArgs e)
        {
            MessageBox.Show("I love you too");
            this.Close();//关闭主窗体
        }

        private void btn_unlove_Click(object sender, EventArgs e)
        {
            MessageBox.Show("还是被你点到了");
            this.Close();//关闭主窗体
        }
    }
}

运行结果:

5. TextBox控件

(1)常用属性:

WorldWrap:指示文本框是否换行;

PasswordChar:让文本框显示一个单一的字符;

ScrollBars:是否显示滚动条;

(2)常用事件:

TextChanged:当文本框中的内容发生改变的时候触发这个事件。

TextBox示例如下:

using System;
using System.Windows.Forms;

namespace WindowsFormsApp1
{
    public partial class Form1 : Form
    {
        public Form1()
        {
            InitializeComponent();
        }

        /// 
        /// 当文本框TextBox中的内容发生改变的时候,将内容赋值给label
        /// 
        /// 
        /// 
        private void txtBox_TextChanged(object sender, EventArgs e)
        {
            label.Text = txtBox.Text;
        }
    }
}

运行结果:

C#学习笔记_第11张图片

6. Timer组件

在指定的时间间隔内做一件指定的事情。

 跑马灯练习:

using System;
using System.Windows.Forms;

namespace WindowsFormsApp1
{
    public partial class Form1 : Form
    {
        public Form1()
        {
            InitializeComponent();
        }

        private void timer1_Tick(object sender, EventArgs e)
        {
            label.Text = label.Text.Substring(1) + label.Text.Substring(0, 1);
        }
    }
}

运行结果:

C#学习笔记_第12张图片

 闹钟练习:

using System;
using System.Media;
using System.Windows.Forms;

namespace WindowsFormsApp1
{
    public partial class Form1 : Form
    {
        public Form1()
        {
            InitializeComponent();
        }

        /// 
        /// 当窗体加载出来的时候  将系统的时间赋值给label
        /// 
        /// 
        /// 
        private void Form1_Load(object sender, EventArgs e)
        {
            label_Time.Text = DateTime.Now.ToString();
        }

        /// 
        /// 每隔一秒钟就把当前的时间赋值给label
        /// 
        /// 
        /// 
        private void timer1_Tick(object sender, EventArgs e)
        {
            label_Time.Text = DateTime.Now.ToString();
            //设置一个固定的时间点作为闹钟
            if(DateTime.Now.Hour==18&&DateTime.Now.Minute==13&&DateTime.Now.Second==10)
            {
                //播放音乐
                SoundPlayer sp = new SoundPlayer();
                sp.SoundLocation = @"C:\音效01_科幻.wav";//播放本地音乐
                sp.Play();
            }
        }
    }
}

运行结果:

C#学习笔记_第13张图片

十二、多线程和Socket网络编程

人通过【电话】可以通信,

程序通过【Socket】进行通信,

Socket(套接字)就是程序间的电话机。

甲和乙打电话(规定好的语言);

电脑和电脑进行联系(协议)。

Socket相关概念

Socket的英文原义是“孔”或“插座”。作为进程通信机制,取后一种意思。通常也称作“套接字”,用于描述IP地址和端口,是一个通信链的句柄。(其实就是两个程序通信用的)

Socket非常类似于电话插座。以一个电话网为例。电话的通话双方相当于相互通信的两个程序,电话号码就是IP地址。任何用户在通话之前,首先要占有一部电话机,相当于申请一个Socket;同时要知道对方的号码,相当于对方有一个固定的socket。然后向对方拨号呼叫,相当于发出连接请求。对方假如在场并空闲,拿起电话话筒,双方就可以正式通话,相当于连接成功。双方通话的过程,是一方向电话机发出信号和对方从电话机接收信号的过程,相当于向Socket发送数据和从Scoket接收数据。通话结束后,一方挂起电话机相当于关闭Socket,撤销连接。

你可能感兴趣的:(C#,c#)