(13)c#传智:访问修饰符,简单工厂模式,序列化与反序列化,部分类,密封类,接口(第13天)(代码片段)

dzweather dzweather     2023-03-12     600

关键词:

内容超级多,慢慢来。。。

    
    深入BinaryFormatter
    


一、访问修饰符


    public: 公共的,公开的
    private:私有的,只能在当前类的内部访问
    protected:受保持的,只能在当前类的内部和该类的子类中访问。
    internal:只能在当前程序集(项目)中访问。
              同一项目中public与internal权限一样。
    protected internal:从当前程序集或派生自包含类的类型访问受保护的内部成员。
    
    internal的感观认识:
        在同一个解决方案中,
        a)在Test项目中添加两个类:
            internal class Person
            public class Student
        b)再新建一个项目Luck,若想从Luck项目中访问Test项目中的Person与Student类,
            首先在Luck项目(在右侧解决方案中右击Luck项目目录中的“引用”),添加
                引用,弹出的窗体中选择Test项目(窗体左侧解决方案中的“项目”中)
            第二步,在Luck项目的最上面添加命名空间:using Luck;
            第三步,在Luck内部就可以直接使用上面Student进行声明赋值,注意的是,
                鼠标指向上面声明代码时,会提示是Test.Student,说明是Test项目的.
                
            但是此时用Person p=new Person();时会发生错误,说明不能访问到项目
            Test中的internal Person。因为internal只能在项目内使用,跨项目会
            提示错误。这就是public与internal的区别。
            
    protected internal感观认识:
        实际上是两种方式都可以用,比较灵活。
        既可是internal即项目内访问,也可以protected在本类及子类中访问(可能跨项目)
        实例:在同一解决项目中有两个项目Test与Luck,其中Test中引用Luck。
        Luck项目:

        namespace Luck
        
            public class BaseClass
             protected internal int myValue = 0; 

            internal class TestAccess
            
                private void Access()
                
                    var baseObject = new BaseClass();
                    //internal同项目,所以下句可以访问
                    baseObject.myValue = 5;
                
            
        


        Test项目,主程序

        using System;
        using Luck;
        namespace Test
        
            internal class Program : BaseClass //继承
            
                private static void Main(string[] args)
                
                    //引用后,可访问Luck项目中的public BaseClass
                    var b = new BaseClass();
                    //Program继承于BaseClass,可访问父类的proctected myValue
                    var InheritObject = new Program();
                    InheritObject.myValue = 10;//因继承而能访问
                    b.myValue = 10;//错误,跨项目只能用继承才能访问
                    Console.ReadKey();
                
            
         

   
    
    internal与protected的访问权限谁大?
        internal仅限于本项目内,出了该项目就不能访问。
        protected是本类及子类,可能没出本项目,也可能出了本项目,只要该类或本类
                 都可访问。
        所以两者各有优势,没有谁大谁小之说。
    
    1)能够修饰类的访问修饰符只有两个:public,internal
        默认的修改是internal,所以一般需人为添加为public。    
    
    2)可访问性不一性
        子类的访问权限不能高于父类的访问权限。
        例:internal class Person
            public class Student:Person 
        错误,子类权限大于父类本想限制在本项目内的权限,会暴露父类成员。

    
二、简单工厂设计模式


    设计模式:设计这个项目的一种方式。程序的巧妙写法,不是算法,是表达方式。
              如同文章写法:总分结构、分总结构、总分总结构、倒叙结构...等等
    程序表达一样,有很多通用的表达方法,把一些相同的表达方法归纳总结成一种,这种就
    形成一种设计模式。同样另一些相同的归纳成另一种设计模式。以此类推...
            
    《C#设计模式》,23种,从头到尾敲一篇。
    设计模式是软件开必过程中经验的积累,特定问题的经过实践的特定方法。
     先看后面实例后,再看前面的文字。。。


    1、什么是工厂?
        1)客户不需要知道怎么做的, 但是不影响使用
        我们身边有很多工厂:酿酒的酒厂, 制衣的衣厂, 加工肉类的肉加工厂等等.这些工厂他
        们到底是怎么酿酒的? 怎么制衣的?怎么加工肉的? 我们并不知道, 也不需要知道. 不知
        道并不影响我们喝酒, 穿衣, 吃肉. 这就是工厂的特点之一.
        2)给你材料, 你去制造出我想要的东西, 至于怎么做, 我并不关心.
        比如肉加工厂---双汇. 牛肉进去出来牛肉火腿肠, 羊肉进去出来羊肉火腿肠, 猪肉进去出
        来猪肉火腿肠. 我不需要知道怎么加工的, 我只需要把材料扔进去, 然后对应的火腿肠就
        出来了. 这就是工厂的第二个特点。
    
    2、什么是设计模式?
        我们基本都知道设计模式有23种。
        设计模式不是语法, 而是一种巧妙的写法, 能够把程序变得更加灵活的写法.
        设计模式有三种: 创建型, 行为型, 结构型. 简单工厂设计模式属于创建型.
        
        但简单工厂设计模式不属于23种设计模式范围内, 属于23种设计模式中工厂设计模式
        里最简单的一种。
    
    3、什么是简单工厂模式?
        简单工厂设计模式, 又叫做静态工厂设计模式
        简单工厂设计模式提供一个创建对象实例的功能,而无需关心其具体实现,被创建实例
        的类型可以是接口、抽象类,也可以是具体的类。
        
    4、简单工厂设计模式的四个要素
        这个很重要, 这也是创建一个简单工厂的步骤:
        1)API接口: 创建一个API接口或抽象类
        2)Impl实现类: 一个或多个实现API接口/抽象类的类
        3)工厂: 定义一个工厂, 用来创建API接口类对象
        4)客户端: 用来调用工厂创建API接口或抽象类的客户端
        
    5、简单工厂创建过程:
        第一步: 定义API接口或抽象类, 并定义一个operate操作方法;
        第二步: 定义API的实现类, 每个实现类单独实现operate方法;
        第三步: 定义工厂类. 工厂类依赖API接口和API的实现类, 简单工厂设计模式是创建型的,
            通常是用来创建实体类. 因此我们定义一个create方法, 来创建实例对象,入参通常
            是一个指定的类型.
        第四步: 定义客户端. 客户端传入一个指定的类型给工厂, 工厂就会创建出对应的实现类.
    
    6、简单工厂设计模式例子
        用户需要指定品牌笔记本。
        工厂万能满足用户需要。
        两者之间,通过工厂来实现。通过特定品牌创建子类,返回通用的父类(子类转换而来)
        
        工厂实现的前提:做好接口或抽象类,并在子类中实现。
        
        用户需求笔记本:   用户需求笔记本<---->父类(子类品牌:Acer,Lenove,Dell...)
    只需提供给用户笔记本的父类,就可以随意定制其子类的笔记本。
    
    先定义工厂父类与各笔记本子类

    public abstract class NoteBook
    
        public abstract void Say();
    
    public class IBM : NoteBook
    
        public override void Say()
         Console.WriteLine("我是IBM笔记本"); 
    

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


    
    再在主程序中,调用父类制作具体的子类笔记本。
 

    private static void Main(string[] args)
    
        Console.WriteLine("请入输入要制作的品牌笔记本:");
        string brand = Console.ReadLine();
        NoteBook n = GetNB(brand);
        n.Say();
        Console.ReadKey();
    


    //核心部分
 

    public static NoteBook GetNB(string brand)
    
        NoteBook nb;
        switch (brand)
        
            case "Dell": nb = new Dell(); break;
            case "Lenovo": nb = new Lenovo(); break;
            default: nb = new IBM(); break;
        
        return nb;
    


    
    
三、值类型与引用类型


    值类型:int,double,decimal,char,bool,enum,struct.(存储上)
    引用类型:string,数组,自定义类,集合,接口.(存储上)
    
    值传递与引用传递
    值类型在赋值的时候,传递是值的本身(复制了原来的一份)
    引用类型赋值时,只是传递了这个对象的引用(原对象未复制),即复制的是指针。
    
    ref:将值传递改变成引用传递.
    
    例:创建一个类

    public class Person
    
        private string _name;
        public string Name  get => _name; set => _name = value; 
    


    
    主函数中运行:

    private static void Main(string[] args)
    
        int n1 = 10, n2 = n1;
        n2 = 20;
        Console.WriteLine("n1:0,n2:1", n1, n2);//n1:10,n2:20
        
        Person p1 = new Person();
        p1.Name = "李四";
        Person p2 = p1;
        p2.Name = "张三";
        Console.WriteLine("p1:0,p2:1", p1.Name, p2.Name);//p1:张三,p2:张三
        TestRef(p1);
        Console.WriteLine(p1.Name);//引用类型,在传参时一样指向同一地址
        
        string s1 = "王五";
        string s2 = s1;//两者一样
        s2 = "邓六";//因字符串不可变性,新赋值将开辟新的空间,地址发生更改,不再同一地址.
        Console.WriteLine("s1:0,s2:1", s1, s2);//s1:王五,s2:邓六

        int n3 = 10;
        TestInt(n3); //值传递
        Console.WriteLine(n3);//10
        
        TestIntRef(ref n3); //引用传递
        Console.WriteLine(n3);//11
        Console.ReadKey();
    

    public static void TestRef(Person p)
     Person p3 = p; p3.Name = "王五"; 

    public static void TestInt(int n)
     n++; 

    public static void TestIntRef(ref int n)
     n++;     

四、序列化与反序列化


    序列化:将对象转化为二进制.
    反序列化:将二进制转化为对象。
    作用:传输数据,因为只有二进制才能被传输。
      从A地将O对象传到B地:
      A地O对象序列化成二进制,B地接受二进制后,反序列化成O对象。最终O对象从A到达了B。
      
    序列化步骤:
    1)对象标记:[Serializable]
    2)使用FileStream流,用BinaryFormatter进行写入流为二进制.
    
    反序列化步骤
    1)使用FileStream流读取
    2)使用Deserialize反序列化,并强制转为对象类型。从而获得对象数据。
    
    例子:下面是序列化后保存到文件,反序列化:把文件读取后反序列到对象.

    internal class Program
    
        private static void Main(string[] args)
        
            Person p = new Person  Gender = 'M', Name = "Luck" ;
            using (FileStream fsw = new FileStream(@"E:\\1.txt", FileMode.OpenOrCreate, FileAccess.Write))
            
                BinaryFormatter bf = new BinaryFormatter();//开始序列化
                bf.Serialize(fsw, p);//自动转p为二进制,用流fsw写入
            
            Console.WriteLine("序列化完毕,去1.txt查看");

            //反序列化
            Person p2;
            using (FileStream fsr = new FileStream(@"E:\\1.txt", FileMode.Open, FileAccess.Read))
            
                BinaryFormatter bf = new BinaryFormatter();
                p2 = (Person)bf.Deserialize(fsr);//返回为object须转换类型
            
            Console.WriteLine(p2.Name);
            Console.ReadKey();
        
    

    [Serializable]
    public class Person
    
        private string _name;
        private char _gender;

        public string Name  get => _name; set => _name = value; 
        public char Gender  get => _gender; set => _gender = value; 
    


    
五、部分类partial


    同一命名空间不能定义两个同名的类.
    
    但实际工作中,一个类很大,需要三个人同时来写这个类,因此出现部分类的概念。
    就是每个人都可以在同一命名空间下写同名的类,但前面加prtial,表明是这个类
    的一部分,三个人写的这个类都可以相互访问,因为这个同名部分的类本质是一个
    类,哪怕分别是private.
    
    当然不能有同名的字段;不能有同名的方法(除非重载
    
    例子:

    internal class Program
    
        private static void Main(string[] args)
        
            Student s = new Student  Id = 12, Name = "比尔" ;//部分类汇总
            s.SayID();
            Console.ReadKey();
        
    

    public partial class Student//部分类
    
        private string _name;

        public string Name  get => _name; set => _name = value; 

        public void Play()
         

        public void SayID()
         Console.WriteLine(this.Id); //可以使用下面部分类中的成员Id
    

    public partial class Student//部分类
    
        private int _id;

        public int Id  get => _id; set => _id = value; 

        //public void Play()  //部分类的方法签名必须不一样
        public void Talk()
         Console.WriteLine(this.Name); //可以使用上面部分类中的成员Name
    


    
六、密封类sealed


    密封者,不泄露,不继承。相当于断子绝孙,不再向下继承传递.
    
    在类前标记为sealed,即为密封类,表明不再向下继承。

    public sealed class Animal
     

    public class Tiger : Animal //错误父类已密封不得继承
     


    
    但是密封类可以从别人处继承而来。

    public class Life
     

    public sealed class Animal:Life//密封类可从别人处继承
         


    
七、重写ToString()方法


    直接输出一个对象时,一般出现的就是ToString,显示的是对象的命名空间。
    所有类型都可以ToString,因为都继承于Object对象中的ToString()
    
    object中有三个虚方法:Equals(object obj)
                          GetHashCode()
                          ToString()
    例子: 

    internal class Program
    
        private static void Main(string[] args)
        
            Rabbit r = new Rabbit();
            Console.WriteLine(r.ToString());//Study Hard
            Console.ReadKey();
        
    

    public class Rabbit
    
        public override string ToString()//重写
         return "Study Hard"; 
    

    
八、接口简介

    接口结束后,C#的基础就讲完了。
    
    1、普通属性与自动属性
          普通属性有字段与属性两者;自动属性只有属性简写,没有方法体
          但两者本质上经反编译后是相同的,普通属性不清洗时可直接用自动属性代替.
          
          所以自动属性只能通过构造函数去限制字段。否则就写成普通属性进行限制

        public class Person
        
            private string _name;

            public string Name//普通属性
            
                get  return _name; 
                set  _name = value; 
            

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


    
    
    2、接口简介
        由于类的单根性,只能继承一个父类。当你想继承两个父类时,就显得尴尬了。
        接口是一个规范,一个能力。
        
        语法:
        [public] interface I...able //接口名
        
            成员;
        
        
        接口中的成员不允许添加访问修饰符,默认就是public.
        
        接口成员不能有定义。不能有方法体;同样不能有字段名,但可以有属性
        这个属性只能是自动属性,因为自动属性没有方法体。
        因为接口起到桥梁作用,不起到存储数据作用。可以限定相互的规范(属性)
        
        接口里面只能有方法、属性、索引器.本质上都是方法,即接口里都是方法。

        public interface IFlyable
        
            void Fly();

            //int ID;//错误不能有字段
            string Name //属性能只写成自动属性
            
                get;
                set;
            

            string GetValue();
            


    
    3、接口的特点
    
        1)接口是一种规范,只要一个类继承了一个接口,这个类就必须实现这个接口中的所有成员
          这和抽象类一样,父类里没有方法体,子类必须重写实现。
        
        2)为了多态,接口不能被实例化,也即:接口不能new(不能创建对象).
            因为接口没有方法体,是不能进行实例创建的。不能实例化的还有静态类,抽象类。
            不同的类用相同的接口,于是调用接口实现多态。
        
        3)接口中的成员不能加访问修饰符,接口中的成员访问修饰符为public,不能修改
            接口中的成员不能有任何实现--光说不做.只是定义了一组未实现的成员。
        
        4)接口中只能有方法、属性、索引器,事件。不能有字段和构造函数。
        
        5)接口与接口之间可以继承,并且可以多继承。可以多接口,但类不能进行多继承。

            public interface M1
            
                void look1();
            

            public interface M2
            
                void look2();
            

            public interface M3
            
                void look3();
            

            public interface ISuperM : M1, M2, M3
            //接口继承,里面隐含look1-3
                void look4();
            

            public class Car : ISuperM
            //四个实现方法缺一不可,否则出错
                //void M1.look1() //出错,必须全部实现.故此方法也得实现
                // 

                void M2.look2()
                 

                void M3.look3()
                 

                void ISuperM.look4()
                 
            


        
        6)接口并不能继承一个类,而类可以继承一个接口。
            类既可以继承类,也可以继承接口。
            接口能够继承接口,但不能继承类。

            public class Person
            
            

            public interface M1:Person//错误,接口不能继承类
            
                void Walk1();
            


        
        7)实现接口的子类必须实现该接口的全部成员。
        
        8)一个类可以同时继承一个类并实现多个接口,如果一个子类同时继承了父类A,
            并实现了接口IA,那么语法上A必须写在IA的前面。
            一句话:同时继承类和接口,则类必须写在前面。
            因为类是单继承,接口可以多继承,这样多个接口可以连续写在后面。

            public class Person
            
            

            public interface Idoable
            
                void Walk();
            

            public class Student : Idoable, Person//错误,接口应在类后
            
                public void Walk()
                
                
            

            public class Student : Person, Idoable
            
                public void Walk()
                
                
            


        
        与其说是面向对象编程,不如说是面向接口编程。
            
        当一个抽象类实现接口的时候,需要子类去实现接口。

            internal class Program
            
                private static void Main(string[] args)
                
                    //IFlyable Ifly=new IFlyable();//错误接口不能实例化
                    IFlyable Ifly = new Person();//Person必须有接口
                    Ifly.Fly();//人类能飞吗?
                    Ifly = new Bird();
                    Ifly.Fly();//鸟儿能飞.    同样的形式不同的结果,多态
                    Console.ReadKey();
                
            

            public class Person : IFlyable
            
                public void Fly()
                 Console.WriteLine("人类能飞吗?"); 
            

            public class Bird : IFlyable
            
                public void Fly()
                 Console.WriteLine("鸟儿能飞"); 
            

            public interface IFlyable
            //接口内部只能是方法:方法、属性(自动属性)、索引器
                //不允许有访问修饰符,默认为public
                void Fly();
            


    
    
    4、接口的练习
    
        麻雀能飞,鹦鹉能飞,鸵鸟不会飞,企鹅不会飞,直升飞机会飞。
        用多态来实现。需要方法、抽象类、接口
       分析:1.用鸟的实体会飞方法不能解决“不会飞”的情况;
             2.用鸟的会飞的抽象方法,不能通用解决不会飞的情况。
        由此:是否会飞是一种能力,要用接口来实现。
        同时不是所有鸟会飞,因此鸟这个父类不得有会飞接口。
        鸟儿可以规定它的共性:生命、翅膀、两只脚等等。
        同时,说话也是一种能力,也可以用接口。
        于是:一个基类:鸟;两种接口:说话能力、飞会能力。
        上面来组装题目。比如,鹦鹉用鸟父类两接口
        直升机用会飞接口

        internal class Program
        
            private static void Main(string[] args)
            //接口的作用就是多态。否则分别创建各自对象,各自方法,代码繁琐混乱
                IFlyable ifly = new Parrot();
                ifly.Fly();//统一对象,统一方法,简单方便
                ISpeak ispk = new Parrot();
                ispk.Speak();
                ifly = new Sparrow();
                ifly.Fly();
                Console.ReadKey();
            
        

        public class Bird
        //只能作父类,不能有会飞接口,因为有些不会飞
            public void HaveWing()
             Console.WriteLine("鸟有翅膀."); 

            public void HaveLife()
             Console.WriteLine("鸟有生命."); 
        

        public interface IFlyable
        //会飞的接口
            void Fly();
        

        public interface ISpeak
        //说话能力不要与飞的能力混合。每种接口能力应清晰有界限
            void Speak();
        //因为有些不能同时具备两种能力。

        public class Parrot : Bird, IFlyable, ISpeak
        
            public void Speak()
             Console.WriteLine("鹦鹉能说话."); 

            void IFlyable.Fly()
             Console.WriteLine("鹦鹉能飞"); 
        

        public class Sparrow : Bird, IFlyable
        
            public void Fly()
             Console.WriteLine("麻雀能飞."); 
        

        public class Ostrich : Bird//没啥能力,只归父类鸟
         

        public class Penguin : Bird//同样没能力
         

        public class Helicopter : IFlyable //不属鸟,只能飞能力
        
            public void Fly()
             Console.WriteLine("直升飞机能飞。"); 
        


        
    5、显式实现接口
        显式实现接口的目的:解决方法的重名问题。
        
        未继承接口时,类中方法是属性类的。但是如果用了接口同名方法后,
        这个类中的方法不再属于类,而是接口的。怎么将原来类的方法不属于
        接口??
          显式地声明一个接口同名方法,即类中在方法前加上接口名,指明这个
        方法是属于接口,原同名的方法仍然还是类中的方法。

 

        internal class Program
        
            private static void Main(string[] args)
            
                IFlyable ifly = new Bird();
                ifly.Fly();//接口的鸟在飞
                Bird f = new Bird();
                f.Fly();//类中的鸟在飞
                f = (Bird)ifly;
                f.Fly();//类中的鸟在飞
                Console.ReadKey();
            
        

        public class Bird : IFlyable
        
            public void Fly()//下面显式声明了Fly,本处Fly则属于类
             Console.WriteLine("类中的鸟在飞"); 

            void IFlyable.Fly()//前面不得用public,否则错误
             Console.WriteLine("接口的鸟在飞"); //显式声明接口方法,属于接口
        

        public interface IFlyable
        
            void Fly();
        


    
    说明:类中默认是private成员,可以添加访问修饰符private(一般都应添加以便显式识别)
        接口中默认是public,如果显式实现接口,7.3版本禁止添加public,会提示错误。
        同样在类中接口显式的实现,相当于接口的延伸,也不得添加public。
        
        但是,如果是隐式实现,必须前面加public,否则会出错。
    
    结论:子类中隐式实现的接口方法,属于子类;应该用public,不然接口无法调用而错误.
          子类中显式实现的接口方法,属于接口;不能用修饰符,默认为public.
    
    可以把接口当作实现接口类的最简的“父类”。IFlyable是Bird的"父类".
    当调用接口的方法时,就会如同抽象类一样,被接口的“子类”同名方法重写了。
    
    什么时候显式的去实现接口:
        当继承的接口中的方法与参数一模一样的时候,要是用显式的实现接口。以便两者区分.
    


九、小结


    什么时候用虚方法来实现多态?
    什么时候用抽象类来实现多态?
    什么时候用接口来实现多态?

        提供的多个类,如果可以从中抽象出一个父类,并且在父类必须写上这几个子类共有的
    方法(提炼出来的方法),此时,
        共有的方法不知道怎么实现,只能在子类去实现,用抽象类;
        共有的方法知道实现,并且还创建父类的对象进行初始化,用虚方法;
            
      接口则是这几个类无法找出共同的父类,但是可以找出它们有共同的能力、共同的行为。
      (鸟与直升机无法找出共同的父类,但可以找出共同的能力--会飞)
            
    练习一:真的鸭子会游泳,木头鸭子不会游泳,橡皮甲子会游泳
        会干什么、能干什么,表明一种能力,用接口。
        尽管可以真鸭子作父类,但不能把游泳作为共同方法,因为木头不会游泳。虚方法pass.
        由于真鸭子需要创建对象,不能用抽象方法。

        internal class Program
        
            private static void Main(string[] args)
            
                ISwimable isw = new RealDuck();
                isw.Swim();//真的鸭子会嘎嘎游
                isw = new RubberDuck();
                isw.Swim();//橡皮鸭子飘着游
                Console.ReadKey();
            
        

        public class RealDuck : ISwimable
        
            public void Swim()
             Console.WriteLine("真的鸭子会嘎嘎游"); 
        

        public class WoodDuck
        
        

        public class RubberDuck : ISwimable
        
            public void Swim()
             Console.WriteLine("橡皮鸭子飘着游"); 
        

        public interface ISwimable
        
            void Swim();
        


    
    分析一:

        public class RealDuck : ISwimable
        
            public void Swim()
             Console.WriteLine("真的鸭子会嘎嘎游"); 
        

        public class RubberDuck : RealDuck, ISwimable
        
        

        public interface ISwimable
        
            void Swim();
        


        橡皮鸭Rubber未实现接口方法Swim,不会报错,因为父类真实鸭RealDuck已经
    实现,由父类继承到子类,子类中就有实现的Swim方法了。
        父类RealDuck中的接口方法Swim,是属于RealDuck,不是ISwimable的。因为它
    是隐式的,所以前面加了public.
    
    分析二:将分析一中的RubberDuck改成下面:

        public class RubberDuck : RealDuck, ISwimable
        
            void Swim()
            
                Console.WriteLine("橡皮同名鸭子");
            
        


        类内Swim是隐式接口方法,属于RubberDuck,所以默认前面是private,由于与父类
    中RealDuck中的Swim重名,当ISwimable isw = new RubberDuck()时,调用:
            isw.Swim();//真的鸭子会嘎嘎游
        可以看到,本类中是private不会调用,直接调用了父类中的真鸭子。
        同时该Swim会警告提示,因为这个重名了,若故意隐藏父类重名应用New来表示:
            private new void Swim() 
        尽管不会警告,仍会提示不会使用,因为是private,改成public,调用上面:
            isw.Swim();//橡皮同名鸭子
        这样,重载就直接调用的是本类橡皮鸭子的Swim了。
        注意,这里不能因重载,前面写成public override void Swim(),这样会出错,
    因为这里override只能与virtual配套使用。即前面要有虚方法。或者是抽象方法。
    


十、GUID(全局唯一标识符)


        GUID(Globally Unique Identifier)全局唯一标识符,是一种由算法生成的二进制
    长度为128位的数字标识符。GUID主要用于在拥有多个节点、多台计算机的网络或系统中。
        在理想情况下,任何计算机和计算机集群都不会生成两个相同的GUID。GUID的总数
    达到了2^128(3.4×10^38)个,所以随机生成两个相同GUID的可能性非常小,但并不为0。
    所以,用于生成GUID的算法通常都加入了非随机的参数(如时间),以保证这种重复的
    情况不会发生。
    
        Guid.NewGuid()  产生一个不会重复的ID

    
十一、项目:超市收银系统


    超市分三大块:
    1)商品:有商品ID(GUID码,唯一码)、价格、上货数量
        为了便于管理,建立一个商品父类,包括上面三个属性内容。
    2)仓库:1存储货物;2提货;3进货。
    3)收银:
    
    第一步:建立商品类,假定商品有Acer、banana、JiangYou(酱油)、Samsung四类
            其中它们的父类ProductFather.

        internal class ProductFather
        //商品父类
            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;
            
        


        //下面四个子类

        internal class Acer : ProductFather
        
            public Acer(string id, double price, string name) : base(id, price, name)
             
        
        internal class JiangYou : ProductFather
        
            public JiangYou(string id, double price, string name) : base(id, price, name)
             
        
        internal class SamSung : ProductFather
        
            public SamSung(string id, double price, string name) : base(id, price, name)
             
        
        internal class Banana : ProductFather
        
            public Banana(string id, double price, string name) : base(id, price, name)
             
        


        
        
    第二步:建立仓库类(重点)
        存储货物,用数组不可取,长度不能伸缩。
        下面用集合也不可取,4种就要4个集合,10000种商品就要10000个集合:(,程序员
    添加商品都会写10000行....
        List<SamSung> lstSam=new List<SamSung>();
        List<Banana> lstBan=new List<Banana>();
        List<JiangYou> lstJY=new List<JiangYou>();
        List<Acer> lstAcer=new List<Acer>();
        //.....
        所以第一步中的商品类父类,ProductFather就很重要了,它屏蔽了各种商品的差异
    性,于是就用父类来解决:
        List<ProductFather> lst1 = new List<ProductFather>();
        但是,这只是一种类型:父类。无法区别子类商品。仓库时货物都是分门别类,排
    列整齐,存储与放置都很方便的。而不是直接用父类(无法区别子类)进行混合存放。
        所以好像又是死路了。用字典键值对,一样,也是有一个商品一个数据,雷同于最
    开始的10000种商品的做法,也行不通。
    
        集合组成的集合:
 

        List<List<ProductFather>> lst2 = new List<List<ProductFather>>();


        注意:lst1是由ProductFather类型元素组成的集合,有四种类型元素;
              lst2是由集合list<ProductFather>组成的。因此list2的元素是集合.
    
        仓库由很多货架组成,每个货架摆放的是同一类型商品。
    因此每一个货架是一个集合,由同一种类型的商品(元素)组成。
    仓库是大集合,由每个货架(集合)作为元素来组合成仓库。题中有商品对应四个货架.
        所以上面集合组成的集合,lst2[0]-lst2[3]分别对应四个货架,是四个集合,
    每一种集合存储对应的一种商品元素.
    
        一句话,现在没添加货物,只是仓库初始化。(以便后加添加货架)
    因为其构造函数,可以如下写四句就是四个货架:

        lst2.Add(new List<ProductFather>());


        但这仍然是有多少商品写多少货架。可是,这个货架的形式一样,对上一句,就可
    以用循环了,四个货架循环四次,10000个货架循环10000次。
    
    流程:仓库用构造函数时,就会创建四个空的货架(无货),还需要添加货物。再通过方法
        进货进行添加到货架。取货时返回到父类

    internal class CangKu
    
        //private List<ProductFather> lis1 = new List<ProductFather>();//与下一句有区别
        private List<List<ProductFather>> lst2 = new List<List<ProductFather>>();//泛型嵌套

        public CangKu()
        
            lst2.Add(new List<ProductFather>());
            lst2.Add(new List<ProductFather>());
            lst2.Add(new List<ProductFather>());
            lst2.Add(new List<ProductFather>());
        //lst[0]Acer, lst2[1]SamSung, lst2[2]JiangYou, lst2[3]Banana

        public void JinPros(string strType, int count)
        //逐步将同种商品入库,生成唯一码对应每一个商品ID
            for (int i = 0; i < count; i++)
            
                switch (strType)//疑惑:同样商品10000种,分支10000种?
                
                    case "Acer":
                        lst2[0].Add(new Acer(Guid.NewGuid().ToString(), 1000, "宏基电脑"));
                        break;

                    case "SamSung":
                        lst2[1].Add(new SamSung(Guid.NewGuid().ToString(), 1000, "三星手机"));
                        break;

                    case "JiangYou":
                        lst2[2].Add(new JiangYou(Guid.NewGuid().ToString(), 1000, "酱油路人"));
                        break;

                    case "Banana":
                        lst2[3].Add(new Banana(Guid.NewGuid().ToString(), 1000, "香蕉你个菠萝"));
                        break;
                
            
        

        public ProductFather[] QuPros(string strType, int count)//返回父类屏蔽子类差异
        //取货,取多个固定大小用数组,从集合中移除有的货物
            ProductFather[] pros = new ProductFather[count];
            for (int i = 0; i < count; i++)
            
                switch (strType)
                
                    case "Acer":
                        if (lst2[0].Count == 0) break;//货物空,自然返回数组中元素为null
                        pros[i] = lst2[0][0];
                        lst2[0].RemoveAt(0);//移除索引0,后面自动填充索引移动,不是元素移动
                        break;              //即原索引1变成移后索引0,后面以此类推

                    case "SamSung":
                        if (lst2[1].Count == 0) break;
                        pros[i] = lst2[1][0];//移除之前判断仓库是否有货,才能安全地移除
                        lst2[1].RemoveAt(0);//也可写成lst2[1].RemoveAt(lst2[1].Count - 1);
                        break;

                    case "JiangYou":
                        if (lst2[2].Count == 0) break;
                        pros[i] = lst2[2][0];
                        lst2[2].RemoveAt(0);
                        break;

                    case "Banana":
                        if (lst2[3].Count == 0) break;//如果一直没货,返回数组元素即为null
                        pros[i] = lst2[3][0];
                        lst2[3].RemoveAt(0);
                        break;
                
            
            return pros;
        

        public void ShowPros()//展示货物有哪些
        
            foreach (var item in lst2)
            
                Console.WriteLine("超市有0货物1个,每个2元", item[0].Name, item.Count, item[0].Price);
            
        
    


    
    
    
    第三步,超市类
        创建仓库(初始化实例),创建超市:进货,出货,计算金额。还有结算打折。
        但是打折有很多种,无法固定,因此需要有一个父类来完善。父类无需知道打折的
    实现,根据输入的选择,由子类来具体实现打折的活动。故父类应是抽象类CalFather.
    与前面的简单工厂设计模式相同:根据用户的输入返回一个父类对象(GetCal)
        在未创建仓库时,仓库中是无货物,只有创建超市时里面创建了仓库,加入了货物,
    此时就可以显示ShowPors(),该方法内部去调用ck.ShowPros();

    internal class SupperMarket
    
        private CangKu ck = new CangKu();

        public SupperMarket()
        //必须与进化中的CangKu类中JinPros()中货物字串相匹配,不然无法进入货架
            ck.JinPros("Acer", 1000);
            ck.JinPros("SamSung", 1000);
            ck.JinPros("JiangYou", 1000);
            ck.JinPros("Banana", 1000);
        

        public void AskBuying()//询问并取货
        
            Console.WriteLine("欢迎光临,请问您需要什么?");
            Console.WriteLine("我们有Acer,SamSung,Jianyou,Banana:");
            string strTypte = Console.ReadLine();
            Console.WriteLine("您需要多少?");
            int count = Convert.ToInt32(Console.ReadLine());
            //取货
            ProductFather[] pros = ck.QuPros(strTypte, count);
            double realmoney = GetMoney(pros);//总金额
            Console.WriteLine("你总共应付0元", realmoney);
            //打折
            Console.WriteLine("选择打折方式:1.不打折,2.打九折,3.打85折,4.买300送50,5.买500送100");
            string input = Console.ReadLine();
            //通过简单工厂的设计模式。根据用户输入获得一个打折对象
            CalFather cal = GetCal(input);//返回父类.
            //调用父类方法。实则在多态在子类中重写。
            double totalMoney = cal.GetTotalMoney(realmoney);
            Console.WriteLine("打折后应付0元.", totalMoney);
            Console.WriteLine("你的购物清单:");
            foreach (var item in pros)
            
                Console.WriteLine("货物编号:0,\\t货物名称:1,\\t货物人价格:2", item.ID, item.Name, item.Price);
            
        

        public CalFather GetCal(string input)//简单工厂类
        
            CalFather cal = null;
            switch (input)
            
                case "1": cal = new CalNormal(); break;
                case "2": cal = new CalRate(0.9); break;
                case "3": cal = new CalRate(0.85); break;
                case "4": cal = new CalMN(300, 50); break;
                case "5": cal = new CalMN(500, 100); break;
            

            return cal;
        

        public double GetMoney(ProductFather[] pros)//计算金额
        
            double realmoney = 0;
            //第一种:有bug
            //for (int i = 0; i < pros.Length; i++)
            //
            //    realmoney += pros[i].Price;//可改成pros[0]
            //

            //第二种:也有bug
            //realmoney = pros[0].Price * pros.Length;

            //第三种:因为返回的父类数组pros[]可能不是满的,甚至全部元素为空。
            int i = 0;
            while (pros[i] != null)
            
                realmoney += pros[i].Price;//考虑到以后可能不同货物,用i而不用0
                if (++i > pros.Length - 1) break;
            
            return realmoney;
        

        public void ShowPros()
        
            ck.ShowPros();
        
    


    
    超市类中要用到简单工作设计模式的打折写法。
    由父类CalFather抽象方法,到三个子类CalNormal,CalRate,CalNM去实现:

    internal abstract class CalFather
    
        /// <summary>
        /// 计算打折后的金额
        /// </summary>
        /// <param name="realmoney">折前金额</param>
        /// <returns>折后金额</returns>
        public abstract double GetTotalMoney(double realmoney);
    
    internal class CalNormal : CalFather
    
        public override double GetTotalMoney(double realmoney)
        
            return realmoney;
        
    
    internal class CalMN : CalFather
    //买M送N
        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; //几个M送几N
            
            else
            
                return realMoney;
            
        
    
    internal 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;
        
    


    
    第四步,在主函数操作
        创建超市对象(内含货物进入),然后显示一下货物。
        然后与用户交互:进行商品交易,金额打折,以及小票显示。

    internal class Program
    
        private static void Main(string[] args)
        
            SupperMarket sm = new SupperMarket();
            sm.ShowPros();
            sm.AskBuying();
            sm.ShowPros();
            Console.ReadKey();
        
    


    
    程序运行操作显示:
        超市有宏基电脑货物1000个,每个1000元
        超市有三星手机货物1000个,每个1000元
        超市有酱油路人货物1000个,每个1000元
        超市有香蕉你个菠萝货物1000个,每个1000元
        欢迎光临,请问您需要什么?
        我们有Acer,SamSung,Jianyou,Banana:
        Acer
        您需要多少?
        11
        你总共应付11000元
        选择打折方式:1.不打折,2.打九折,3.打85折,4.买300送50,5.买500送100
        3
        打折后应付9350元.
        你的购物清单:
        货物编号:ef76a5c7-29c3-45b7-8938-d8aa8a8896df,  货物名称:宏基电脑,      货物人价格:1000
        货物编号:72128793-908c-496d-9195-50e228bffe27,  货物名称:宏基电脑,      货物人价格:1000
        货物编号:faec6c84-c3e7-4830-8714-2148f9156602,  货物名称:宏基电脑,      货物人价格:1000
        货物编号:7483ad5e-78d2-4f05-a2ed-99319bf9c3d9,  货物名称:宏基电脑,      货物人价格:1000
        货物编号:a1908cf1-5763-4e42-9d88-425dd10d15cf,  货物名称:宏基电脑,      货物人价格:1000
        货物编号:7a0497fb-9bff-450b-aa14-5da0ad27b4c0,  货物名称:宏基电脑,      货物人价格:1000
        货物编号:47e3cf90-a072-4a16-8f3a-a1387c50aac2,  货物名称:宏基电脑,      货物人价格:1000
        货物编号:2b0c57fe-0732-4fe1-a182-c8ba302bf578,  货物名称:宏基电脑,      货物人价格:1000
        货物编号:6cd32184-26e8-4598-b3cf-8f724e8b6153,  货物名称:宏基电脑,      货物人价格:1000
        货物编号:c8cc38a7-fd17-49a4-ac68-14b83f961620,  货物名称:宏基电脑,      货物人价格:1000
        货物编号:b2cc1f9c-94ac-47d5-abb7-aaa871d4e9aa,  货物名称:宏基电脑,      货物人价格:1000
        超市有宏基电脑货物989个,每个1000元
        超市有三星手机货物1000个,每个1000元
        超市有酱油路人货物1000个,每个1000元
        超市有香蕉你个菠萝货物1000个,每个1000元    
    
    

c#面向对象10--简单工厂设计模式

...模式17.责任链模式18.命令模式19.备忘录模式20.状态模式21.访问者模式22.中介者模式23.解释器模式设计模式简介|菜鸟教程(runoob.com) 查看详情

c#类的修饰符都有哪些?

...在按值封送对象时,就会创建一个该对象的副本,并将其序列化传送到服务器。任何对该对象的方法调用都是在服务器上进行的。[STAThread]:是Single-ThreadedApartment单线程套间的意思,是一种线程模型(线程模式用于处理组件在多... 查看详情

C# 中类和变量的默认访问修饰符? [复制]

】C#中类和变量的默认访问修饰符?[复制]【英文标题】:DefaultaccessmodifierforclassandvariableinC#?[duplicate]【发布时间】:2014-12-1108:45:52【问题描述】:什么是C#中类和变量的默认访问修饰符?对于Class,有两种类型的访问修饰符:Pulic... 查看详情

C#中接口成员的访问修饰符

】C#中接口成员的访问修饰符【英文标题】:AccessmodifiersoninterfacemembersinC#【发布时间】:2010-11-0720:21:09【问题描述】:我收到以下属性的编译错误。错误是:“修饰符‘public’对此项无效”publicSystem.Collections.Specialized.StringDictiona... 查看详情

为啥接口成员没有访问修饰符? [复制]

】为啥接口成员没有访问修饰符?[复制]【英文标题】:Whydointerfacemembershavenoaccessmodifier?[duplicate]为什么接口成员没有访问修饰符?[复制]【发布时间】:2011-08-2719:13:31【问题描述】:可能重复:Whycan\'tIhaveprotectedinterfacemembers?作... 查看详情

c#五大修饰符

修饰符访问权限public关键字是类型和类型成员的访问修饰符。 公共访问是允许的最高访问级别。 对访问公共成员没有限制private私有访问是允许的最低访问级别。 私有成员只有在声明它们的类和结构体中才是可访问... 查看详情

为啥我们不能在 C# 中覆盖方法时更改访问修饰符?

】为啥我们不能在C#中覆盖方法时更改访问修饰符?【英文标题】:Whycan\'twechangeaccessmodifierwhileoverridingmethodsinC#?为什么我们不能在C#中覆盖方法时更改访问修饰符?【发布时间】:2011-09-0809:04:04【问题描述】:在C#中,我们不能... 查看详情

c#中的默认访问修饰符

1.命名空间下的元素的默认访问修饰符 public:同一程序集的其他任何代码或引用该程序集的其他程序集都可以访问该类型或成员。internal:同一程序集中的任何代码都可以访问该类型或成员,但其他程序集不可以访问。 2.各... 查看详情

单例简单工厂模式。

...使用时再创建对象。*/publicclassSingleton2{ // privateStringname;//修饰符数据类型属性名; privatestaticSingleton2singleton; privateSingleton2(){ } publicstaticSing 查看详情

c#基础访问修饰符ref与out标志枚举等等

C#基础(一)访问修饰符、ref与out、标志枚举等等一、访问修饰符在C#中的访问修饰符有:private、protected、internal、publicpublic:公共类型,同一程序集或其他程序集都可以访问此成员private:私有类型,同一类里或结构里才可以访问... 查看详情

c#访问修饰符

C#中所有类型和类型成员都具有可访问性级别,用来控制是否可以在您程序集的其他代码中或其他程序集中使用它们。 可使用一下访问修饰符指定声明类型或成员时类型或成员的可访问性。 public同一程序集中的任何其他... 查看详情

c#编程(二十四)----------修饰符

...符修饰符应用于说明public所有类型或成员任何代码均可以访问该项protected类型和内嵌类型的所有成员只有派生的类型能访问该项internal所有类型或成员只能包含它的程序集中访问该项private类型和内 查看详情

c#基础知识八之访问修饰符

1. 类的访问修饰符修饰符访问权限无或者internal只能在同一个程序集中访问类public同一个程序集或引用该程序集的外部都可访问类abstract或internalabstract只能在同一程序集中访问类,且该类不能被实例化,只能被继承publicabstract... 查看详情

C#:为啥我必须在类的变量中使用公共访问修饰符?

】C#:为啥我必须在类的变量中使用公共访问修饰符?【英文标题】:C#:WhydoIhavetousethepublicaccessmodifierinclass\'svars?C#:为什么我必须在类的变量中使用公共访问修饰符?【发布时间】:2011-09-2703:57:26【问题描述】:好吧,我是初学... 查看详情

访问修饰符是 C# 中方法签名的一部分吗?

】访问修饰符是C#中方法签名的一部分吗?【英文标题】:IsaccessmodifierpartofMethodSignatureinC#?【发布时间】:2015-09-2103:54:34【问题描述】:MSDN这里的https://msdn.microsoft.com/en-us/library/ms173114.aspx说像“private/protected”这样的访问修饰符... 查看详情

为啥在 C# 中需要提及访问修饰符来实现接口属性?

】为啥在C#中需要提及访问修饰符来实现接口属性?【英文标题】:WhyinC#doesoneneedtomentiontheaccessmodifierforimplementationofaninterfaceproperty?为什么在C#中需要提及访问修饰符来实现接口属性?【发布时间】:2018-08-2106:43:28【问题描述】:... 查看详情

在 C# 中限制表单类属性的访问修饰符

】在C#中限制表单类属性的访问修饰符【英文标题】:RestrictAccessModifierofFormClassPropertiesinC#【发布时间】:2019-04-2711:14:22【问题描述】:我创建了一个继承Windows.FORM的DLL类,我想限制它的propertiesAccessModifier比如Size(width-height)&For... 查看详情

Java 和 Scala 中 C# 的访问修饰符的等价物是啥?

】Java和Scala中C#的访问修饰符的等价物是啥?【英文标题】:WhataretheequivalentsofC#\'saccessmodifiersinJavaandScala?Java和Scala中C#的访问修饰符的等价物是什么?【发布时间】:2014-05-2223:34:01【问题描述】:我以前使用C#工作过,现在我花... 查看详情