• 15-01-11 C# 面向对象 10


    类中的成员,如果不加访问修饰符,默认是private;

    私有的,只能在当前类的内部访问;

    理论上是给每个私有的字段配备一个公有的属性;

    静态函数中只能访问静态成员;静态类中只能有静态成员;非静态函数既可以访问静态成员,也可以访问非静态成员,非静态类中既可以有静态成员,也可以有非静态成员;

    this的作用1.当前类的对象,是为了把字段(属性)和局部变量区分开来;字段和属性是和方法平级的,而局部变量是在方法里面的;

    namespace(命名空间),用于解决类重名问题,可以看做类的文件夹;

    命名空间里面有很多很类;命名空间其实就是相当于项目;

    如果我们要使用一个新类,但是这个新类不存在上面的命名空间里,这个时候我们首先要引用这个类的命名空间;

    类是属于命名空间(项目)的,如果没有引用相应的命名空间的话,我们就不能使用这个类;

    如何引用类的命名空间,

    1.把光标放到类的名称下,会看到左下角有个蓝色的小块块,光标移过来点开,就可以引用命名空间

    2.把光标放到类的名称下,alt+shift+F10;

    3.把命名空间记住,直接在上面打出来;

    如果在自己的两个项目中,我要在02项目中引用01项目,

    第一步,添加引用;在02的项目文件的引用里,右键,添加引用,默认选的是项目,选中引用的项目,点确定,这个时候发现在02项目的引用中有了01项目;(系统自带的少了这一步)

    第二步,在上面引用命名空间,

    在C#中数据类型确切的分为值类型和引用类型, 区别 1.值类型和引用类型,在内存上存储的地方不一样,2.在传递值类型和传递引用类型的时候,传递的方式不一样,值类型我们称之为值传递,引用类型我们称之为引用传递

    在内存中我们人为了得分成了5块,我们程序员常用的就是堆,栈,静态存储区域,静态存储区域用来存储静态成员,堆栈就是用来存储我们这些数据类型的数据

    值类型:int,double,bool,char,decimal,struct,enum

    引用类型:string,自定义类,数组

    值类型的值是存储在内存中的栈上的,引用类型的值是存储在内存的堆中,

    int number = 10; 10存在栈中,栈内存空间的名字是number 

    string s = "123"; "123"存在堆中,s在栈中,但是名字为s的栈空间中,存的是"123"在堆中的地址;

    Person p = new Person(); p.Name = "张三"; new Person()存在堆中,new Person()才是一个对象,p在栈中,但是名字为p的栈空间中,存的是new Person()在堆中的地址;Name = "张三"也在堆中

    值类型的值是存在内存中的栈中,引用类型的值存在内存中的堆中,但是引用类型也在栈上开辟了空间,存的是这个值在堆中的引用(地址);

    字符串区别于别的类型有一个很特别的性质,叫

    1.字符串的不可变性, int n1 = 10; ni = 20;    在堆中的10变成了20;老值10,不存在了; string s1 = "张三"; s1 = "李四"; 当你给字符串重新赋值之后,老值"张三"并没有销毁,而是重新开辟一块堆空间存储新值;此时s1在栈中的地址由原来的"张三"的地址变成了"李四"的地址;此时张三还在内存中 因此对字符串循环赋值的话,会在堆内存中产生大量的垃圾; 当程序结束后,GC扫描整个内存,如果发现有的空间没有被指向,则立即把它销毁;

    string s1 = "张三"; string s2 = "张三";    理论上在堆中有2块空间存储"张三",在栈中有两块空间s1,s2分别指向各自的"张三";但事实不是这样的,事实上在堆中只开辟了一块空间,叫做"张三" ,s1,s2同时都指向张三;s1,s2在堆中的地址是一样的,在这时候s1="123" 原来的"张三"是不会变的,因为字符串的不可变性,因此会在堆空间中重新开辟一块空间存储"123";

    如何看变量在内存中的地址,生成运行,调试菜单,窗口,即时里面,在即时窗口里面输入&变量名,回车就能看到;有两个地址,上面那个是栈中的地址,下面那个是堆中的地址

    2.我们可以将字符串看做是char类型的只读数组; 一个string类型可以看做是很多很多char类型组成,很多很多的char类型可以看做是一个数组;

    只读的意思是只可以访问,但不可以改变;

    string s = "abcdefg";既然可以把s当做一个数组来处理,那就意味着可以通过索引来访问其中的某一个元素; Console.WriteLine(s[0]);  //成功输出a

    如果要把第一个字符a改变为b;首先要想办法访问到第一个字符; s[0] = 'b';//报错,因为string是char类型的只读数组;

    如果非要将字符串的第一个字符变'b';

    1.首先将字符串转换为char类型的数组;把它转换为一个真正的数组,那么既可读,又可写了;

    char[] chs = s.ToCharArray(); chs[0] = 'b';

    2.将字符数组转化为字符串;

    s = new string(chs);

    ToCharArray()将字符串转化为char[]数组; new String(char[] chs)能够将char[]转化为字符串;

    当程序中要对字符串进行大量的重新赋值,拼接等操作的时候,如果用string,在堆内存中会产生大量的垃圾 因此我们可以用StringBuilder这个类;

    StringBuilder sb = new StringBuilder();

    记录程序运行的时间,我们呢可以用StopWatch这个类;

    StopWatch sw = new StopWatch();//创建一个计时器;

    sw.Start();  //开始计时;

    sw.Stop(); //结束计时;

    sw.Elapsed 获取测量所用的时间;

    str += i;字符串的追加; sb.Append(i);追加;

    为什么string追加速度这么慢,因为它每次都要在内存中开辟空间;stringbuilder没有开空间; 虽然中间用的是stringbuilder但最后还是把它转化string, sb.ToString();

    一切类型都可以通过ToString()转化为字符串类型;

    str.Length;  调用字符串的属性得到字符串的长度;

    str.ToUpper();  将字符串转化为大写形式;

    str.ToLower();  将字符串转化为小写形式;

    str.Equals(str1,StringComparison.OrdnialIgnoreCase); 忽略大小写的比较两个字符串;

    char[] chs = {'_','+','-','*',' '}; 

    str.split(chs,StringSplitOptions.RemoveEmptyEntries);在str里把字符数组里有的字符给去掉,返回值是string[];

    两个小黄块就是枚举类型

    str.split(new char[]{'_','+','-','*',' '},StringSplitOptions.RemoveEmptyEntries); //等效的;

    str.Contains("老赵");判断字符串里有没有老赵;

    str.Replace("老赵","**"); 将字符串中的老赵替换为**;

    Substring和Split在字符串中经常会用到这两个方法; str.Substring(1,2); 1是要截取开始的索引,2是要截取的长度;

    str.StartsWith("今天");判断字符串是不是以"今天"开始;

    str.EndsWith("今天");判断字符串是不是以"今天"结尾;

    str.IndexOf('天'); 拿到'天'在字符串中首次出现的索引;返回值int; 如果找不到就返回-1;

    str.IndexOf('天',2); 从第二个索引开始找天首次出现的索引,返回值int;  如果找不到就返回-1;

    str.LastIndexOf('天');找天在字符串中最后一次出现的索引;

    string path = @"c:ac........射雕英雄传.wav";

    int idndex = path.LastIndexOf("\");

    path = path.SubString(index+1);  //获取文件名;

    str.Trim();//去掉字符串的前面和后面的空格

    str.TrimStart();//去掉字符串的前面的空格

    str.TrimEnd();//去掉字符串的后面的空格

    string.IsNullOrEmpty(str);判断字符串是不是空值或者null;

    string[] str ={"张三","李四","王五","赵六","田七"};

    string strNew = string.join("|",str);  //得到张三|李四|王五|赵六|田七

    string strNew = string.join("|","张三","李四","王五","赵六","田七");也可以这样写,因为前面有params可变参数;

    string在运行(赋值,拼接)的时候会产生新的实例,而StringBuilder不会产生新的实例

    File类是一个静态类;

    File.ReadAllLines(path,Encoding.Default) 

    //path是文件的路径;读取文件中的所有的行,返回类型是string[];Encoding.Default用来解决乱码;

    要改变一个字符串,除了重新赋值之外,可以先把它变成一个字符数组,在通过循环或者调用方法改变字符数组,再把这个字符数组转化为字符串;

    .net类库提供的字符串方法还是很强大的;

    封装就是把一个功能写成一个方法调用,具体这个功能怎么实现的不必知道,就像调用.net类库提供的方法一样;

    每写一个类应该新建一个类文件去写;

    public class Student:Person

    {  

        private int _id;   

        public int Id   

       {     

         get{return _id;}     

         set{_id = value;}   

        }       

        public void Study()   

       { 

         Console.WriteLine("学习");  

        } 

       //public Student(string name, int age,char gender,int id)   

       //{   

       //this.Name = name;   

       //this.Age = age;   

       //this.Gender = gender;   

       //this.Id = id;   

       //  }                                      

      //这些代码里的大部分在父类里已经写过了,如果用父类无参的构造函数,一行都省不了;因此要调用父类有参的构造函数                                          

      //这样一来,构造函数也执行了,对象也创建了,同时,三行代码也不需要写了。     

        public Student(string name, int age,char gender,int id)   //运行到这里的时候,跳到父类的构造函数;   

        :base(name,age,gender)   

        {       

          this.Id = id;   

        }                                                             

        public new  void Test()    

        {       

           Console.WriteLine("测试1");    

       }

    }

          public class Teacher:Person

        {   

           public Teacher(string name,ing age,char gender,int salary):base(name,age,gender)    

           {       

             this.Salary =salary;   

           }

            private int _salary;  

            public int Salary   

          {     

            get{return _salary;}   

           set{_salary = value;}   

           }

           public void Teach()   

           {      

             Console.WriteLine("教学");

           }                       

             public  new void Test()    

            {      

              Console.WriteLine("测试2");

            }   

    }

    public class Driver:Person

     {   

          private string _driveTime;   

          public string DriveTime   

          {    

             get{return _driveTime;}    

             set{_driveTime = value;}

           }

           public void Drive()   

           {     

              Console.WriteLine("开车");   

            }        

           public Driver(string name,ing age,char gender,int driveTime)

          :base(name,age,gender)   

           {     

                this.DriveTime =driveTime;  

            }

             public  new void Test()  

           {      

                Console.WriteLine("测试3");   

           }  

          }

          构造函数可以解决对象初始化时候的代码冗余,

         :this可以解决构造函数重载的时候的冗余代码;

         继承是用来解决类与类之间的代码冗余;把几个类中共重复的成员单独的拿出来封装成一个类,作为这几个类的父类;

          public class Person

         {   

          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 string _gender;   

         public string Gender   

         {     

            get{return _gender;}     

            set{_gender = value;}   

         }

       public void CHLSS()   

       {     

          Console.WriteLine("吃喝拉撒睡");   

        }

       public Person(string name,int age,char gender)   

        {       

          this.Name = name;       

          this.Age = age;       

           this.Gender = gender;  

         }       

         // public Person()   

        // {

        //   }

         public void Test()    

         {      

            Console.WriteLine("测试");  

          }

    }

     智能提示的正方体是方法的意思;

    子类既可以由父类的成员,也可以有自己的成员;

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

    Student,Teacher,Drive  子类(派生类)  

    Person                        父类(基类)

    我们看子类在父类那里继承过来了什么,就看子类对象能访问到父类的什么;能访问到的表示继承过来了;

    上面那个例子,子类继承了父类的属性和方法;子类没有继承父类的私有字段;

    1.继承的单根性,一个子类只能有一个父类;

    2.继承的传递性,A继承B,B继承C   A不仅能访问B的成员,A也能访问C的成员;

    查看类图:可以看出各个类的关系;右键项目名,视图,查看类图;

    如果子类继承了父类的构造函数,那么创建子类对象的时候,应该会调用到父类的构造函数;

    子类没有继承父类的构造函数;但是子类创建对象的时候,先会执行父类的构造函数,执行完父类的构造函数,再来执行自己的构造函数, 执行父类的构造函数就是创建了一个父类对象,

    当我们创建子类对象的时候,首先会在子类的内部创建一个父类的对象出来,这样才能用到父类的属性和方法;不然子类对象里面没有父类的对象,怎么调用父类的属性和方法;

    因此父类里写了一个有参的构造函数后,父类无参的构造函数就被干掉了,就不能创建父类的对象出来了(因为子类创建对象的时候,调用的是父类无参的构造函数); 如果父类中重新写了一个有参数的构造函数之后,那个无参的就被干掉了,子类就调用不到了,所以子类会报错,

    解决办法

    1.在父类中重新写一个无参数的构造函数; //一般不去这么做;

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

    ctrl+K+D代码对齐;

    本来子类调用的是父类无参的构造函数,现在写了个有参的父类的构造函数,原来的无参的父类构造函数被干掉了,因此调不到原来的无参的构造函数了, 所以去调用有参的构造函数,使用关键字:base();

    Student s = new Student("学生",18,'男',101);

    Object类是所有类的基类;如果你没有让一个类去继承另外一个类,那么这个类默认就继承于Object;

    在C#中所有类都直接或者间接继承Object类;

    如果子类的成员名称和父类的成员名称一样,会把从父类继承过来的一样名字的成员隐藏掉;隐藏的后果是调用不到父类的那个重名的成员了;  

    public  new void Test()把父类中重名的成员隐藏掉了;加了new不报警告错误了;

    new 1.创建对象 2.隐藏从父类那里继承过来的同名成员

    隐藏的后果,子类调用不到父类的被隐藏的成员;

    因此,在继承的时候,尽量不要把子类的成员名字写的和父类的成员名字一样;

    Object也是引用类型,因为它是类;

  • 相关阅读:
    电脑一族,打电脑时候的健康的坐姿
    根据时间戳,增量同步数据的解决办法
    写在前面
    《T-GCN: A Temporal Graph Convolutional Network for Traffic Prediction》 论文解读
    关于Graph Convolutional Network的初步理解
    图形学_opengl纹理映射
    推荐算法_CIKM-2019-AnalytiCup 冠军源码解读_2
    推荐算法_CIKM-2019-AnalytiCup 冠军源码解读
    leetcode_雇佣 K 名工人的最低成本(优先级队列,堆排序)
    图形学_Bezier曲线
  • 原文地址:https://www.cnblogs.com/hhsfrank/p/4216485.html
Copyright © 2020-2023  润新知