(11)C#传智:里氏转换、Protected、ArrayList与HashTable、文件管理Path与File、编码(第11天)

    继续深入内容提示:ArrayList、Hashtable、Path、File、Encoding、System.Buffer
    


一、里氏转换

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

        public class Student : Person //继承
        {
            public void StudentSay()
            { Console.WriteLine("我是子类学生"); }
        }

    定义:
    1)子类可以赋值给父类。若需要父类作参数时,可以用子类代替

        String.Join(string separator,params object[] values)
        string.Join("|",new string[]{"1","2","3"}) 这里用的object子类string


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

        Person p = new Student();
        Student s = (Student)p;//装有子类,可转同样子类
        Teacher t = (Teacher)p;//错误,p中装的学生,非老师,不能转换
        s.StudentSay();//正确
        t.TeacherSay();//错误,此处不能调用

    
    is与as
    is:  表示类型转换。转换成功返回true,否则false.
    as:  表示类型转换。转换成功返回对应对象,否则返回null.

        Person p = new Student();
        if (p is Student)//为true
        { Student s = (Student)p; s.StudentSay(); }
        else Console.WriteLine("不能转换");
        Student s1 = p as Student;
        s1.StudentSay();
        Teacher t = p as Teacher;//不成功,null
        t.TeacherSay();//错误


        
    子类对象可以调用父类对象的成员,但父类对象永远只能调用自己的成员。
    
    例子:有五个类继承于Person:

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

        public class Student : Person //继承
        {
            public void StudentSay()
            { Console.WriteLine("我是学生."); }
        }

        public class Teacher : Person //继承
        {
            public void TeacherSay()
            { Console.WriteLine("我是老师."); }
        }

        public class Beauty : Person //继承
        {
            public void BeautySay()
            { Console.WriteLine("我是美女."); }
        }

        public class HandsomeMan : Person //继承
        {
            public void HandsomManSay()
            { Console.WriteLine("我是帅哥."); }
        }

        public class Animal : Person //继承
        {
            public void AnimalSay()
            { Console.WriteLine("我是野兽."); }
        }


    在主程序中,用子类赋值给基类数组,再将基类强制转换显示:

        Person[] ps = new Person[10];
        Random r = new Random();
        for (int i = 0; i < ps.Length; i++)
        {
            int n = r.Next(1, 7);
            switch (n)
            {//用子类赋值给父类
                case 1: ps[i] = new Student(); break;
                case 2: ps[i] = new Teacher(); break;
                case 3: ps[i] = new Beauty(); break;
                case 4: ps[i] = new HandsomeMan(); break;
                case 5: ps[i] = new Person(); break;
                default: ps[i] = new Animal(); break;
            }
        }
        for (int i = 0; i < ps.Length; i++)
        {//强制转换成子类,再显示
            if (ps[i] is Student) ((Student)ps[i]).StudentSay();
            else if (ps[i] is Teacher) ((Teacher)ps[i]).TeacherSay();
            else if (ps[i] is Beauty) ((Beauty)ps[i]).BeautySay();
            else if (ps[i] is HandsomeMan) ((HandsomeMan)ps[i]).HandsomManSay();
            else if (ps[i] is Animal) ((Animal)ps[i]).AnimalSay();
            else ps[i].PersonSay();
        }


    
    注意:所谓强制转换,并没有改变对象本身,只是改变了引用而已。
        用子类z赋值给父类f时,父类f引用就指向了子类z,此时若强制将父类f转为子类z,
        即就是将引用指向子类z,而子类z真实存在是可行的。所以装有子类的父类是可以
        强制转回子类的。
          类似大学生(子类)可伪装到小学生(父类)中去,大学生没变.若这个"伪小学生"强
        制转换为大学生(父转子),是可以的。因为本身就是大学生嘛。
  

     
二、Protected受保护的


    可以在当前类的内部及继承的子类中进行访问,但不能在类外进行访问.
    权限大小: public>protected>private  类似茅台内部特供仅内部使用
    
    例子:

        public class Person
        {
            protected string _name;//受保持的

            public string Name//类内可访问protected
            { get { return _name; } set { _name = value; } }
        }

        public class Student : Person //继承
        {
            public void Test()
            { Console.WriteLine(_name); }//继承可访问受保持的_name
        }


        //在Main()中:

        Person p = new Person();
        Console.WriteLine(p._name);//错误,类外不能访问
        new Student().Test();//正确

    
三、集合ArrayList与HashTable


    数组是相同类型的一组数据。 限制:长度不可变,类型单一。
    集合:长度可任意改变,类型随便。
    ArrayList 导入using System.Collections;
    
    ToString() 转换为字串。控制台输出时默认进行转为字串
        当为集合类型时,转换出来的是命名空间
    
    例子:
        //先添加一个类

        public class Person
        {
            public void Test()
            { Console.WriteLine("类中方法"); }
        }


        //Main()中

        ArrayList lst = new ArrayList();
        Person p = new Person();//下面加入元素类型不限
        lst.Add(1); lst.Add("abc"); lst.Add(3.2); lst.Add('男');
        lst.Add(p);//对象
        lst.Add(new int[] { 1, 2, 3, 4 });//数组
        lst.Add(lst);//添加lst.ToArray()则为System.Object[]
        for (int i = 0; i < lst.Count; i++)
        {
            if (lst[i] is Person) ((Person)lst[i]).Test();
            else if (lst[i] is int[])
            {
                for (int j = 0; j < ((int[])lst[i]).Length; j++)
                { Console.Write("{0},", ((int[])lst[i])[j]); }
                Console.WriteLine();
            }
            else { Console.WriteLine(lst[i]); }
        }


        //结果:
        1
        abc
        3.2
        男
        类中方法
        1,2,3,4,
        System.Collections.ArrayList    
    
    结论:不限个数,不限类型,添加随意方便。但对于array及ArrayList拿出来时麻烦。
        故单个元素用Add(),若集合时用AddRange(Icollections c)

        ArrayList lst = new ArrayList();
        lst.Add(88);
        lst.AddRange(new int[] { 2, 3, 4 });
        lst.AddRange(lst.ToArray());
        for (int i = 0; i < lst.Count; i++)
        { Console.WriteLine(lst[i]); }
        //lst.Clear();//清空元素
        //lst.Remove(元素);//删除实指的元素
        //lst.RemoveAt(0);//删除索引为0的元素
        //lst.RemoveRange(0,3);//删除索引范围,4个元素
        //lst.sort();//升序排列,类型不同可能引发异常
        //lst.Reverse();//反转
        //lst.Insert(1,value);//将value插入位置
        //lst.InsertRange(0,values);//指定位置插入多个元素(集合)
        //lst.Contains(value);//是否包含元素

        

        //结果: 
        88
        2
        3
        4
        88
        2
        3
        4        
    
    1、构造函数
        ArrayList()    实例为空,容量根据元素自动增加
        ArrayList(System.Collectons.ICollection c) 复制已有的集合来构造新的实例集合
        ArrayList(int capacity)   用指定的容量来初始化一个空的实例
    
    2、属性
        Capacity:容量.容量超限时,会自动增加空间以适应新元素。
        Count:  实际元素数,小于容量。
        IsFixedSize:是否固定大小。固定大小用于节约内存,不能再添加或移除元素,但可以修改.
        IsReadOnly:是否只读。不允许添加、移除或修改.
        IsSynchronized:访问Array是否同步(线程安全),默认false.
        SyncRoot:获取能同步访问ArrayList的对象。多线程需锁定以确保每线程修改正确的同步。
        Item[int index]  设置/获取该索引的元素。
    
    3、方法
        Add(object value)  添加单个元素。类型不限,可以为null,亦可重复.
        AddRange(System.Collectons.ICollectoin c) 添加集合中实际元素,集合不能为null。
        
        Insert(int index,object value) 在指定索引处插入元素
        Insert(int index,System.Collectons.ICollecton c) 指定索引处插入集合中的多个元素
        SetRange(int index,System.Collections.ICollection c) 对指定位置设置集合元素
                  注意,Insert插入后面元素后移,而SetRange不是后移而是覆盖。
        GetRange(int index,int count)  取得指定范围集合,返回新集合。
        
        Remove(object obj)   移除指定的第一个匹配元素
        RemoveAt(int index)    移除指定索引的元素
        RemoveRange(int index,int count) 移动指定索引开始到后面个数的元素
        
        Reverse()  整个ArrayList中元素反转
        Reverse(int index,int count)  指定索引开始到后面个数的元素,这个范围的元素反转
        
        Sort()   排序.  可能引导异常
        Sort(System.Collectons.IComparer comparer)  用指定的比较器来实现比较排序
        Sort(int index,int count,System.Collections.IComparer comparer) 指定范围和比较器排序
        
        ArrayList.Repeat(object value,int count)   用指定重复数量的元素,赋值给另一个集合
        
        ToArray()  将集合元素转为object数组
        ToArray(Type type)  将集合元素转为指定类型的数组。上面返回是object,不实用,这个具体指明类型。
                       尽管指定的类型,但返回的是Array为object,还须对整个数组进行转换,且指定为原类型
        //例子,

        ArrayList al = new ArrayList();
        al.AddRange(new string[] { "1", "2", "3", "4" });
        string[] n = (string[])al.ToArray(typeof(string));
        //上面三个string任一改成int,将异常,例如下面
        //int[] n = (int[])al.ToArray(typeof(int));
        string s = string.Join("-", n);//1-2-3-4
        ArrayList al2 = new ArrayList(new int[] { 1, 2, 3, 4 });
        int[] n2 = (int[])al2.ToArray(typeof(int));//改string异常
        string s2 = string.Join("-", n2);//1-2-3-4        
        Console.WriteLine(s);//1-2-3-4   

     
        
        ArrayList.FixedSize(System.Collections.ArrayList list) 固定大小ArrayList
                    固定元素防止添加或删除,但可以修改
        IList.FixedSize(System.Collections.IList list) 同上
        
        TrimToSize()  将容量设置为实际数目Count(节约部分空余空间).若用Clear清空后,
                      再进行TrimToSize剪裁,容量不是0而是初始化时的容量大小。
        Clear()  移除清空所有元素
        Contain(object item)  是否存储元素item
        
        BinarySearch(object value) 搜索元素,返回索引。负值则无该元素
        BinarySearch(object value,System.Collectons.IComparer comparer) 用指定的比较器进行搜索
        BinarySearch(int index,int count,object value,System.Collectons.IComparer comparer)
                             在指定范围,用指定搜索器查找元素.成功返回索引,失败为负数
                             
        IndexOf(object value)  搜索指定value,返回第一个匹配项的索引.失败返回-1
        IndexOf(object value,int startIndex)  指定起始索引开始搜索
        IndexOf(object value,int startIndex,int count)  指定范围
        LastIndexOf(object value)  搜索value,返回最后一个匹配项的索引.失败返回-1
        LastIndexOf(object value,int startIndex)
        LastIndexOf(object value,int startIndex,int count)
        
        CopyTo(Array array) 将整个ArrayList复制到兼容的一维Array(覆盖)
        CopyTo(Array array,int arrayIndex) 整个ArrayList复制到一维指定索引开始的Array
        CopyTo(int index,Array array,int arrayIndex,int count)
             从ArrayList指定索引开始,复制到Array指定的索引开始且指定个数.
        //复制中是覆盖,不是插入     

        ArrayList al = new ArrayList() { "aa", "bb", "cc", "dd" };
        String[] s = new String[15], s1 = new string[] { "e", "f", "g", "h" };
        s1.CopyTo(s, 0);
        al.CopyTo(1, s, 3, 1);
        Console.WriteLine(string.Join("-", s));//e-f-g-bb-----------
        al.CopyTo(s, 3);
        Console.WriteLine(string.Join("-", s));//e-f-g-aa-bb-cc-dd--------
        al.CopyTo(s);
        Console.WriteLine(string.Join("-", s));//aa-bb-cc-dd-bb-cc-dd--------
        al.CopyTo(s, 13); //错误,13+4-1=16>15 索引超限


        
    4、长度问题Array+List=ArrayList?
        capacity  容量可以包含的元素个数
        count     实际可以包含的元素个数
        每次count超过capacity,capacity自动申请翻倍增加空间,确保长度够用。
        

        ArrayList alst = new ArrayList();
        Console.WriteLine("{0},{1}", alst.Capacity, alst.Count);//0,0
        alst.Add(1);
        Console.WriteLine("{0},{1}", alst.Capacity, alst.Count);//4,1
        alst.AddRange(new int[] { 2, 3, 4, 5, 6, 7 });
        Console.WriteLine("{0},{1}", alst.Capacity, alst.Count);//8,5
        alst.AddRange(new int[] { 8, 9, 10, 11, 12, 13, 14, 15 });
        Console.WriteLine("{0},{1}", alst.Capacity, alst.Count);//16,15
        alst.AddRange(new int[] { 16, 17 });
        Console.WriteLine("{0},{1}", alst.Capacity, alst.Count);//32,17
        Console.ReadKey();


    
    5、练习
        1、创建集合,添加一些数字,并求平均值与和
        

        ArrayList alst = new ArrayList();
        alst.AddRange(new int[] { 22, 32, 88, 12, 234, 998 });
        int sum1 = 0, sum2 = 0;
        foreach (int i in alst)
        { sum1 += i; }
        for (int i = 0; i < alst.Count; i++)
        { sum2 += (int)alst[i]; }//返回object,须转换int
        //尽管object是int父类,但object是由前子类int赋值而来,故可父转子类
        Console.WriteLine("{0},{1},{2}", sum1, sum2, sum1 / alst.Count);


        
        注:最大值和最小值,可以先排序后,取最前面(最小值)和最后面(最大值)。
             alst.Sort();//alst[0]--最小值,alst[alst.count-1]最大值
            也可以通过比较得出,但每次比较需要由object转为int进行比较,较为麻烦。
            泛型可以解决上面麻烦。
    
        2,初建长度为10的集合,随机存入10个数字(0-9),但要求所有数字不重复

        ArrayList alst = new ArrayList();
        Random r = new Random();
        for (int i = 0; i < 10; i++)
        {
            int rNum = r.Next(0, 10);//下句若重复,则一直产生随机
            while (alst.Contains(rNum)) rNum = r.Next(0, 10);
            alst.Add(rNum);
        }
        Console.WriteLine(string.Join("", alst.ToArray()));

    

    
四、哈希表HashTable


    1、类似上面集合,但具体有“键-值”对。
        根据键key去找值value: ht[Key]=Value
        注意:key键必须是唯一的,不能重复。value值可以相同。
             因为根据key找值,key相同时(例如三个相同)就无定位对应的value,到底用哪一个key呢?
        
        foreach(var item in 集合){...}
        C#是强类型语言,必须首先明确定义变量的类型(js是弱类型语言)。var是可变类型。
        var由值来推断变量类型,所以用var声明变量时必须同时赋初值,否则报错。
        问题: for与foreach的效率谁更高?

        Hashtable ht = new Hashtable();
        ht.Add(1, "a"); ht.Add(2, '男'); ht.Add(3, "c"); ht.Add(false, "d");
        for (int i = 0; i < 4; i++)
        {//因ht[0]无此元素不显示;值d只能由ht[false]进行引用.
            Console.WriteLine(ht[i]);//  a b c
        }//实际上把i当作key使用其值,ht[key]=value
        Console.WriteLine("------");
        foreach (var item in ht)
        {//显示4个命名空间:System.Collections.DictionaryEntry
            Console.WriteLine(item);
        }//因为隐式使用item.ToString(),不会显示key或value
        Console.WriteLine("------");
        ht.Add(5, "e"); ht.Add(6, "f"); ht.Add(7, "g");
        foreach (var item in ht.Keys)
        {//即使添加3个,空间没有翻倍,仍是实际大小4+3=7个
            Console.Write(item);//765321False
        }
        Console.WriteLine("\n----");
        ht.Add(8, "h"); ht.Add(9, "j");
        Console.WriteLine(ht.Count);//9
        foreach (var item in ht.Values)
        {//再次添加突破8,仍为实际大小7+2=9个元素
            Console.Write(item);//jhgfecbad
        }//foreach的顺序似乎无从判定


    
    2、添加元素的2种方式:
        1) ht.Add(key,value); 若已有key,直接异常
        2) ht[key]=value;     若已有key,则value覆盖前面key的value值。
    
    3、常用方法
        ContainsKey(object key) 是否包含key.此方法类同Contains(Key key),但更明确。 
        ContainsValue(object value)
        Clear()  清空
        Remove(object key) 由键key来定位移除valu值
    


五、文件管理


    1、Path类   using System.IO;
        Path.GetFileName(string path) 获得路径中的文件名(含扩展)
        Path.GetFileNmaeWithoutExtension(string path) 取得文件名不含扩展
        Path.GetExtension(string path) 取得扩展名
        Path.GetDirectoryName(string path) 取得文件夹(后面无\)
        Path.GetFullPath(string path) 取得全路径(绝对路径)
        Path.Combine(string path1,string path2) 组合成路径,若两部间无\则自动添加
    
    2、File类  using System.IO;  同样为静态类
        File.Create(string path)  创建一个文件,有则无提示覆盖。创建时间一样,但修改时间不一样。
        File.Delete(string path)  彻底删除一个文件(回收站无)。若文件正被其它使用,将引发异常。
        File.Copy(string sourceFileName,string destFileName) 文件从源处复制到目标处
        File.Move(string sourceFileName,stirng destFileName) 剪切文件从源到目的
        
        File.ReadAllText(string path) 读取文本文件里原样内容。自动打开且关闭,返回string 
        File.ReadAllLine(string path) 按行读取返回字串数组。最后一行无内容则省略。
        File.ReadLines(string path)  按行读取返回由每行组成的集合。除最后一行空行外其它窄也为元素。
        
        File.WritAllText(string path,string contents) 将所有内容写入文件。覆盖式
        File.WriteAllLines(string path,string[] contents) 将字串数组逐行写入,覆盖式。
        
        File.AppendAllText(string path,string contents) 追加文本到文件末尾
        File.Exist(string path)  判断文件是否存在


File.ReadLines和File.ReadAllLines区别:
    两者都读取文本文件:
    1、返回类型:
    File.ReadLines返回一个IEnumerable,
    而File.ReadAllLines返回一个string[]数组。
    2、内存使用:
    File.ReadLines在读取文件时使用延迟加载的方式,每次只读取一行,可以减少内存的使用。
    而File.ReadAllLines会一次性将整个文件读入内存,如果文件较大,可能会占用较多的内存空间。
    3、迭代方式:
    File.ReadLines通过迭代器的方式逐行读取文件,可以在循环中逐行处理文件内容。
    而File.ReadAllLines会将整个文件读入内存后返回一个字符串数组,需要一次性读取所有行。
    4、异常处理:
    File.ReadLines在遇到无法访问的文件时,会抛出IOException异常。
    而File.ReadAllLines在遇到无法访问的文件时,会抛出FileNotFoundException异常。
    

// 逐行读取文件内容
foreach (string line in File.ReadLines("AA.txt"))
{
    // 处理每一行的内容
    Console.WriteLine(line);
}

// 一次性读取文件的所有行
string[] lines = File.ReadAllLines("example.txt");
foreach (string line in lines)
{
    // 处理每一行的内容
    Console.WriteLine(line);
}


    

    
六、编码


    编码:将字符以什么形式的二进制进行编码
    最开始美国人发明了计算机,只须要a-ZA-Z,0-9等128个字符,这个128字符编码形式称为ASC码。
    计算机传到东欧,东欧一些音标需要更多的字符来表示,将128字符扩展到256位,称为ASCII码。
    随着计算机传播,更多的国家要使用,需要更多的字符编码表示,如中国的GB2312码,它是简码。
    台湾与香港需要用繁体,即BIG5码。还有日本、韩国。。。等等
    为了适合全球各个国家,统一制定了Unicode码,每个国家都在其中可以使用,但缺点是解码慢。
    特别是如今的网络时代,更需要精简,把字符以更小的字节保存以利网络更快的传输。
    于是出现了UTF-8码,还有UTF-16等等
        
    常见问题:乱码
        原因:保存文件时采用的编码,与打开时所用的编码不一致,即解码错误,就产生的乱码
        例如,保存文件时用的是UTF-8,但打开时不用UTF-8编码,而是用GBP2312,于是就显示乱码
    
    取得编码的方式:Encoding.Default、Encoding.UTF8、Encoding.GetEncoding("GBK")、
            Encoding.GetEncoding("GB2312")等等。
    
    GBK(Chinese Internal Code Specification)汉字内码扩展规范
        对GB2312编码的扩展,包括繁体BIG5编码,以及全部中日韩汉字。
    Encoding.Default缺省编码与系统设置的区域相关,一般中国win10默认GBK编码,也可人为去设置
    成UTF8更适合UTF8。所以编码使用中特别注意不同机器使用的编码方式可能不同。
    
    字节数组的读取与写入
    1)字节写入:File.WriteAllBytes() 
        字串--->字节数组:先将字串转化为字节数组。即Encoding.Default.GetString(字符串)
        
    2)字节读取:File.ReadAllBytes() 
        字节数组--->字串:读取出来是字节数组,需要转换即Encoding.Default.GetString(字节数组)
        
    记事本:文本文件,一般作用五种编码形式(打开txt后,选择文件->另存,弹出窗体下面有编码)
        五种:ANSI、UTF8、UTF16BE、UTF16LE、带BOM的UTF8。
        因此,按上面五种编码存储的文本,都可以正确显示识别。但若以UTF7编码存储,显示只能按上面
        五种进行编码显示,故显示的是乱码。
        
        例:先将字串按UTF7转为字节数组,存储在文件文件中。手动打开查看是乱码。
            再用程序以default编码进行读取,与上面手动查看一样,是乱乱,只有按原样产UTF7读取才正常

        string p = @"E:\1.txt";
        byte[] b = Encoding.UTF7.GetBytes("常练常记常用");
        File.WriteAllBytes(p, b);//UTF7写入后,打开是乱码。若无该文件则新建并写入
        b = File.ReadAllBytes(p);
        Console.WriteLine(Encoding.Default.GetString(b));//+Xjh+w144i7BeOHUo-
        Console.WriteLine(Encoding.UTF7.GetString(b));//常练常记常用


            
        

  • 0
    点赞
  • 2
    收藏
    觉得还不错? 一键收藏
  • 0
    评论

“相关推荐”对你有帮助么?

  • 非常没帮助
  • 没帮助
  • 一般
  • 有帮助
  • 非常有帮助
提交
评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包
实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

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

余额充值