C#面向对象编程基础 I

时间:2020-11-26 17:21:21

一.面向对象OOP

三大特点:数据的封装、继承、多态


二.继承

1.语法

class ChildClass : ParentClass


2.举例

public class Pet
    {
        public string Name;
        public void PrintName()
        {
            Console.WriteLine("Pet's name is" + Name);
        }
    }


    public class Dog:Pet
    {


    }
    public class Cat:Pet
    {


    }


    class Program
    {
        static void Main(string[] args)
        {
            Dog dog = new Dog();
            dog.Name = "Jack";
            dog.PrintName();
            Cat cat = new Cat();
            cat.Name = "Tom";
            cat.PrintName(); 
        }
    }


3.特殊的基类

Object类是所有类的共同基类,是唯一的非派生类。


4.规则

一个类只能继承一个父类。


三.隐藏方法

1.语法

屏蔽数据成员:在派生类中声明名称和类型相同的成员

屏蔽函数成员:在派生类中声明新的带有相同函数签名的成员

让编译器知道:可以添加new关键字,否则会有警告


2.举例

public class Pet
    {
        public string Name;
        public void PrintName()
        {
            Console.WriteLine("Pet's name is " + Name);
        }
    }


    public class Dog:Pet
    {
        new public void PrintName()
        {
            Console.WriteLine("宠物的名字是" + Name);
        }

    }
    public class Cat:Pet
    {


    }


    class Program
    {
        static void Main(string[] args)
        {
            Dog dog = new Dog();
            dog.Name = "Jack";
            dog.PrintName();
            Cat cat = new Cat();
            cat.Name = "Tom";
            cat.PrintName(); 
        }
    }


四.虚方法和多态

1.设计原则:依赖倒置原则

程序设计要依赖于抽象类(Pet),而不依赖于具体类(Dog)


2.基类的引用

当用基类类型的引用指向派生类时,仅仅能访问派生类中的基类部分

例如上面的例子,如果把dog的引用改为从基类Pet引用,那么之前在Dog中的隐藏方法就不能访问

......

class Program
    {
        static void Main(string[] args)
        {
            Pet dog = new Dog();
            dog.Name = "Jack";
            dog.PrintName();
            Cat cat = new Cat();
            cat.Name = "Tom";
            cat.PrintName(); 
        }
    }

运行结果中,dog的名字就是英文形式,而不是中文形式


3.统一提高效率

需要一个容器(比如数组)保存所有的基类(Pet)


4.子类具有差异


5.虚方法和多态的武器

可以解决既有统一的方法属性,又可以体现派生类的个性

声明为virtual的方法就是虚方法

基类的虚方法可以在派生类中用override进行重写

C#面向对象编程基础 I


因为为了统一管理,一般都是用采用基类引用,所以再加上虚方法,就会达到兼顾统一又张扬个性的目的,而此时的情况更符合下图的描述

C#面向对象编程基础 I

即,当pet调用speak方法时,由于是虚方法,所以程序会去找实际是dog派生类,所以就会调用dog的重写方法

但是,如果是dog直接调用speak方法,程序就直接调用speak方法


6.例如

public class Pet
    {
        public string Name;
        public void PrintName()
        {
            Console.WriteLine("Pet's name is " + Name);
        }
        virtual public void Speak()
        {
            Console.WriteLine(Name+" is speaking");
        }

    }


    public class Dog:Pet
    {
        new public void PrintName()
        {
            Console.WriteLine("宠物的名字是" + Name);
        }
        override public void Speak()
        {
            Console.WriteLine(Name + " is speaking "+"WoWo");
        }

    }
    public class Cat:Pet
    {
        override public void Speak()
        {
            Console.WriteLine(Name + " is speaking " + "MiMi");
        }

    }


    class Program
    {
        static void Main(string[] args)
        {
            Pet dog = new Dog();
            dog.Name = "Jack";
            dog.PrintName();
            dog.Speak();
            Cat cat = new Cat();
            cat.Name = "Tom";
            cat.PrintName();
            cat.Speak();
        }
    }


7.管理

例如

public class Pet
    {
        public string Name;
        public void PrintName()
        {
            Console.WriteLine("Pet's name is " + Name);
        }
        virtual public void Speak()
        {
            Console.WriteLine(Name+" is speaking");
        }
    }


    public class Dog:Pet
    {
        public Dog(string name)
        {
            Name = name;
        }

        new public void PrintName()
        {
            Console.WriteLine("宠物的名字是" + Name);
        }
        override public void Speak()
        {
            Console.WriteLine(Name + " is speaking "+"WoWo");
        }
    }
    public class Cat:Pet
    {
        public Cat(string name)
        {
            Name = name;
        }

        override public void Speak()
        {
            Console.WriteLine(Name + " is speaking " + "MiMi");
        }
    }


    class Program
    {
        static void Main(string[] args)
        {
            Pet[] pets = new Pet[] { new Dog("Jack"), new Cat("Tom") };


           
for(int i=0;i<pets.Length;++i)
            {
                pets[i].Speak();
            }

           
        }
    }


8.关于虚方法的其它知识点

重写虚方法时,基类方法不能是private

static方法不能重写

方法、属性、索引器、事件,都可以用虚方法或重写


五.派生类和构造函数

1.构造函数就是初始化一个构造对象

在执行派生类的构造函数体之前,将会隐式或显示调用基类构造函数

如下图的调用顺序所示

C#面向对象编程基础 I


2.调用基类构造函数

C#面向对象编程基础 I


3.调用当前类的其它构造函数

C#面向对象编程基础 I


六.抽象类和抽象方法

1.抽象方法语法

abstract  public void Func();

在基类不可以有函数体,只能在派生类中用override重写


2.当一个class含有一个抽象方法,就是抽象类

abstract class Pet

{

......

}

抽象类可以全是抽象成员,也可以全是普通成员(可以不声明abstract),也可以是它们的组合

抽象类的抽象成员在派生类中必须用override来重写


七.密闭类和密闭方法

1.用sealed关键字即为密闭类和密闭方法

不希望其他人继承修改某些类,或者不希望其他人重写某些方法,不希望派生出子类


2.如果一个基类方法不希望子类对其重写,不声明为virtual就行

如果是派生类方法不希望子类对其重写,同时是override重写,就可以用sealed机制


八.接口

1.接口是一种引用类型,指定一组函数成员但是不实现这些函数


2.语法

interface ICatchMice // 接口名称一般以I开头

{

  void CatchMice();  // 默认public,但不能加public

}


3.接口是用来被继承类实现

Cat : ICatchMice

{

  public void CatchMice() {......}

}


4.接口也是一种引用类型

Cat c = new Cat();

ICatchMice ic = (ICatchMice)c;

c.CatchMice ();  //通过对象调用

ic.CatchMice ();  //通过接口调用


5.接口可以实现一个类有多个接口

Cat : Pet, ICatchMice, IClimbTree

{

public void CatchRat(){...}

public void ClimbTree(){...}

...

}


6.例如

namespace PetShop
{
    interface ICatchMice
    {
        void CatchMice();
    }
    interface IClimbTree
    {
        void ClimbTree();
    }

    abstract public class Pet
    {
        public Pet(string name)
        {
            _name = name;
        }
        protected string _name;
        public void PringName()
        {
            Console.WriteLine("Pet's name is " + _name);
        }
        abstract public void Speak();
        virtual public void Move()
        {
        }
    }
    public class Dog:Pet
    {
        public Dog(string name):base(name)
        {
        }
        new public void PrintName()
        {
            Console.WriteLine("宠物的名字是" + _name);
        }
        sealed override public void Speak()
        {
            Console.WriteLine(_name + " is speaking:" + "WoWo");
        }
        public override void Move()
        {
        }
    }
    public class Labrador:Dog
    {
        public Labrador(string name):base(name)
        {
        }
    }
    public class Cat:Pet,ICatchMice,IClimbTree
    {
        public Cat(string name):base(name)
        {
        }
        public override void Speak()
        {
            Console.WriteLine(_name + " is speaking:" + "MiMi");
        }
        public void CatchMice()
        {
            Console.WriteLine("Catch mice");
        }
        public void ClimbTree()
        {
            Console.WriteLine("Climb tree");
        }

    }


    class Program
    {
        static void Main(string[] args)
        {
            Pet[] pets = new Pet[] { new Dog("Jack"), new Cat("Tom") };
            for (int i=0; i<pets.Length;++i)
            {
                pets[i].Speak();
            }
            Cat c = new Cat("Tom2");
            IClimbTree climb = (IClimbTree)c;
            c.ClimbTree();
            climb.ClimbTree();
            ICatchMice catchM = (ICatchMice)c;
            c.CatchMice();
            catchM.CatchMice();

        }
       
    }
}

运行结果

C#面向对象编程基础 I


7.小贴士

接口并不是对一个类的补充描述,它是泛化的类,不是具体的事物,高级用法还可以是行为的组合。

比如,公司要招人,所签的合同就是接口。

屋主要打扫卫生,用猫或狗甚至人来抓屋子里的老鼠,这个抓老鼠就是接口。

学生要请家教来上课,上课就是接口,这个老师不满意可以换下一个老师。

人饿了,要用网站来订餐,这其中的饿了吃饭就是接口,必胜客不满意可以换麦当劳。


九.结构和类

1.不同点

结构是值类型(在栈中),类是引用类型(在堆中)

结构不支持继承,类支持继承

结构不能定义默认构造函数,编译器会定义


2.适用场合

结构:由于分配内存快,作用域结束即被删除,不需要垃圾回收,由于小型数据结构。

但传递过程中会复制,应该使用ref提高效率。


类:由于其他的需要继承体系的场合


3.例如

在PetShop中定义

struct fish

{

int weight;

int size;

int type;

}


之后在Cat中调用


4.小贴士

普通编程中很少用到结构,一般高手才会用结构。