C#里氏转化原则、抽象类、接口

C#中可以使用一个父类类型的变量来引用一个子类类型的对象,即将子类对象当作父类类型使用,这就是所谓的里氏转化原则

1.子类对象可以直接赋值给父类变量。并且通过父类变量可以调用子类中的方法,这个子类的方法要是对父类的方法进行重写的方法,于父类无关系的方法不能被调用

namespace 里氏转化
{
    class Program
    {
        static void Main(string[] args)
        {
            Animal an1 = new Dog();//里氏转化,父类类型的变量来引用一个子类类型的对象
            an1.Shout();//父类的变量调用子类的方法,此方法是对父类的方法进行了重写,其他无关系的方法将不能被调用
            Console.ReadKey();
            
        }
    }
    class Animal
    {
        public virtual void Shout()//虚方法
        {
            Console.WriteLine("动物在叫");
        }
    }

    class Dog : Animal
    {
        public override void Shout()//对父类的方法进行了重写,从而实例化的时候,父类类型的变量可以直接调用子类对象的方法
        {
            Console.WriteLine("狗在叫");
        }
    }
}


2.将父类变量转换为子类类型

要想将父类变量转换为子类类型,只能在一种特殊情况下实现,即父类变量引用的是当前子类对象

namespace 里氏转化父类转子类
{
    class Program
    {
        static void Main(string[] args)
        {
            Animal an1 = new Dog();
            an1.Shout();
            Dog dog = (Dog)an1;//父类对象an1强转为子类类型
            dog.Shout();
            dog.Run();
        }
    }
    class Animal
    {
        public void Shout()
        {
            Console.WriteLine("叫");
        }
    }

    class Dog : Animal
    {
        public void Run()
        {
            Console.WriteLine("跑");
        }
    }
}


在继承关系中当子类对象赋值给父类变量的情况下,父类变量可以通过强转指向子类变量。但是如果不知道他们是否是继承关系的情况下,强转类型就可能出现未知错误。所以可以使用c#提供的is和as关键字。

is和as都是可以用来判断父类变量是否指向子类,as关键字除了判断之外,还有直接转换的功能。如果判断成功就直接进行类型转换,如果判断失败就返回null。

namespace is和as的关键字
{
    class Program
    {
        static void Main(string[] args)
        {
            Animal an1 = new Dog();//下面的is语句和as语句使用的前提必须要有这句话,否则都是报错
            bool result = an1 is Dog;//申请一个bool类型的变量去判断an是否指向dog类型
            if (result)//判断结果
            {
                Console.WriteLine("an1能转换Dog类型");
            }
            else
            {
                Console.WriteLine("an1不能转换Dog类型");
            }

            Dog dog = an1 as Dog;//as语句除了判断还可以直接转换
            if (dog != null)
            {
                Console.WriteLine("可以转换,已经转换成功");
            }
            else
            {
                Console.WriteLine("转换失败");
            }
            Console.ReadKey();
        }
    }
    class Animal
    {
        public void Shout()
        {
            Console.WriteLine("jiao");
        }
    }
    class Dog:Animal
    {
        public void Run()
        {
            Console.WriteLine("pao");
        }
    }
}


抽象类

当定义一个类是,常常需要定义一些方法来描述该类的行为特征,但有时这些方法的实现方法是无法确定的

例如在前面定义Animal类时,Shout()方法用于表示动物的叫声,但是针对不同的动物,叫声也是不同的,因此在Shout()方法中无法准确描述动物的叫声。就像这种情况,C#允许在定义方法时不写方法体,不包含方法体的方法为抽象方法,抽象方法必须使用asbstract关键字来修饰,例如abstract void Shout();//定义首相方法Shout()

当一个类中包含了抽象方法,该类必须使用abstract关键字来修饰,使用abstract关键字修饰的类为抽象类。

在定义抽象类的时候需要注意,包含抽象方法的类必须声明为抽象类,但抽象类可以不包含抽象方法,另外,抽象类是不可以被实例化的,因为抽象类中有可能包含抽象方法, 抽象方法是没有方法体的,不可以被调用,如果想调用抽象类中定义的方法,则需要创建一个子类,在子类中实现抽象类的抽象方法。

在调用时,必须要把抽象类的抽象方法都实现,否则就会直接报错。在子类中实现抽象类的方法时,要用重写的方式,关键字为override


namespace 抽象类
{
    class Program
    {
        static void Main(string[] args)
        {
            Dog dog = new Dog();//实例化,因为抽象类是不能被实例化的,我们在子类中已经增加了抽象方法的具体方法体,所以我们直接实例化子类就可以了
            dog.Run();//调用方法
            dog.Shout();
            Console.ReadKey();
        }
    }
    abstract class Animal//定义一个抽象类
    {
        public abstract void Shout();//定义一个抽象方法,抽象方法是不包含方法体的
        public abstract void Run();//定义一个抽象方法
    }

    class Dog : Animal//定义一个类,继承这个抽象类,在这个子类中实现抽象类中的所有抽象方法,否则就会报错
    {
        public override void Run()//重写这个方法,实现这个抽象方法的具体功能,增加方法体
        {
            Console.WriteLine("pao");
        }
        public override void Shout()//重写这个方法,实现这个抽象方法的具体功能,增加方法体
        {
            Console.WriteLine("jiao");
        }
    }
}


接口

接口的关键字是interface,如果一个抽象类中的所有方法都是抽象的,则可以将这个类用另外一种方式来定义,即接口。在定义接口时,需要使用interface关键字来声明。

定义方法

interface Animal

{

void Breathe();//定义抽象方法

void Run();//定义抽象方法

}

因为接口中定义的方法和变量都包含一些默认修饰符public,且不能显示添加修饰符。另外,接口中的抽象方法不需要用asbstract关键字修饰(而抽象类中的抽象方法必须要用asbstract修饰,且子类实现抽象方法时用override关键字重写抽象方法)

由于接口中的方法都是抽象方法。因此不能通过实例化对象的方法来调用接口中的方法,此时需要定义一个类,并实现就扣的所有方法

接口特点:

1.接口中的所有方法都是抽象的,因此接口不能被实例化

2.一个类可以是实现多个接口,被实现的多个接口之前用逗号分隔

3.一个接口可以继承多个接口,接口之前用逗号分隔(一个类不可以继承多个类,只能有一个直接父类)


using System;
using System.Collections.Generic;
using System.Linq;
using System.Text;
using System.Threading.Tasks;

namespace 接口
{
    class Program
    {
        static void Main(string[] args)
        {
            Dog dog = new Dog();
            dog.Run();
            dog.Shout();
            Cat cat = new Cat();
            cat.Jump();
            cat.Run();
            cat.Shout();
            Fish fish = new Fish();
            fish.Jump();
            fish.Run();
            fish.Shout();
            fish.Swim();
            Console.ReadKey();
        }
    }
    interface Animal//定义一个接口
    {
        void Shout();
        void Run();
    }
    interface LandAnimal//定义一个接口
    {
        void Jump();
    }
    interface MarineAnimal:Animal,LandAnimal//一个接口可以继承多个接口
    {
        void Swim();
    }

    class Dog : Animal//类与接口的关系
    {
        public void Shout()
        {
            Console.WriteLine("我是狗,我会叫");
        }
        public void Run()
        {
            Console.WriteLine("我是狗,我会跑");
        }
    }

    class Cat : Animal,LandAnimal//一个类可以实现多一个接口
    {
        public void Shout()
        {
            Console.WriteLine("我是猫,我会叫");
        }
        public void Run()
        {
            Console.WriteLine("我是猫,我会跑");
        }
        public void Jump()
        {
            Console.WriteLine("我是猫,我会跳");
        }
    }

    class Fish: MarineAnimal//实现这个接口,但是这个接口继承了多个接口,如果要实现这个接口,需要把它继承的接口的方法都实现
    {
        public void Shout()
        {
            Console.WriteLine("我是鱼,我会叫");
        }
        public void Run()
        {
            Console.WriteLine("我是鱼,我会跑");
        }
        public void Jump()
        {
            Console.WriteLine("我是鱼,我会跳");
        }
        public void Swim()
        {
            Console.WriteLine("我是鱼,我会游泳");
        }
    }
}

发布于 2019-12-11