【c#】抽象方法与虚方法、abstract与virtual、抽象方法与隐藏方法、抽象类与接口

在c#的学习过程中,我们总是会遇到很多看着很相似的语法规则亦或是各种方法,以至于我们看到它们总是摸棱两可的,下面,我来说说我理解的抽象方法与虚方法、abstract与virtual、抽象方法与隐藏方法、抽象类与接口。

抽象方法与虚方法

要想掌握c#里面的抽象方法和虚方法,我们至少要了解它们的定义。
抽象方法:在基类中定义的,它要求继承该基类的派生类一定要重写(overried)的方法,该方法使用关键字abstract定义。同时记得抽象方法只能在抽象类里面定义。
虚方法:在基类中定义的,它允许在继承该基类的派生类中重写的方法(注意我这里的用词,是允许,而上面的抽象方法是一定),该方法使用关键字virtual定义。

区别:

  • 抽象方法一定要在派生类中重写该方法。虚方法可以在派生类中去重写方法,也可以不在派生类中去重写方法,这取决于程序员自己根据项目的需求。
  • 抽象方法一定是在抽象类中声明的,也就是说,如果一个类包含抽象方法,那么这个类也是抽象的,应该声明为抽象类。
  • 抽象方法的函数体是没有代码去实现的,或者可以直接省略掉函数体。虚方法的函数体里面是有代码去实现的,而且程序员必须去实现虚方法的函数体。
  • 虚方法是可以直接被调用的,而抽象方法则不可以。

下面演示虚方法被直接调用的例子:

using System;
namespace Test
{
	class A
	{
		public virtual void function()
		{
   			 Console.WriteLine("我是虚方方法,可以被直接调用");
		}
	}

	class B
	{
		static void Main()
		{
			A.a = new A();
			a.function();	
			Console.ReadKey();	
		}	
	}
	
}

上面代码的运行结果为:
在这里插入图片描述

abstract与virtual

通过上面的抽象方法和虚方法,我们肯定对abstractvirtual有所了解了,有的人认为我前面都写了抽象方法和虚方方法的区别了,还写abstractvirtual干嘛?是不是吃饱了没事做。我回答是:你猜?好了,废话不多说,直接进入正题!

abstract和virtual两个关键字都是用来修饰基类的,abstract可以修饰类,即抽象类咯,也可以修饰方法,即抽象方法咯;virtual只能修饰方法,即虚方法。派生类可以通过覆盖其基类里面abstract修饰的抽象方法或者virtual修饰的虚方法,去重写两种方法。

区别:

  • abstract修饰的抽象方法必须被派生类重写,virtual修饰的虚方法可以被派生类重写(不是必须的)。
  • 只有在abstract修饰的抽象类里面才能有abstract修饰的抽象方法。
  • abstract修饰的抽象类是不能去实例化一个具体的对象的。
  • abstract修饰的抽象方法一定不能去实现,而是要求在派生类里面去重写实现;而virtual修饰的虚方法一定是要去实现的,即使你很懒,去添加一对大括号都是允许的。

抽象方法与隐藏方法

抽象方法在这我就像不续写了,要不真的是吃饱了没事做了,其实说真的,c#里面的隐藏方法用法还是比较简单吧,一般不会和抽象方法弄混,那就来直接说隐藏方法吧

首先要了解隐藏方法是什么?
隐藏方法:它是使用new关键字定义,在派生类中定义的和基类中的某个方法具有相同名字的方法。

直接上一个例子大家理解一下吧!

using System;

namespace YinCang
{
	class A
	{ 
		public void Test()
		{
			Console.WriteLine("我是基类A");
		}
	}

	class B : A
	{
		new public void Test() //或者这样写:public new void Test()
		{
			Console.WriteLine("我是派生类B");
		}
	}

	class C
	{
		static void Main(string[] args)
		{
			A a = new A();//这里是A的实例
			A b = new B();//这里还是A的实例,但引用了派生类B对象--若不理解这里,看下面注解
			B c = new B();//这里才是B的实例
			a.Test();//基类的实例调用基类的方法----所以打印:我是基类A
			b.Test();//基类的实例调用基类的方法----所以打印:我是基类A
			c.Test();//派生类B的实例调用派生类的方法---所以打印:我是派生类B
            Console.ReadKey();
		}
	}
}

/*注解:
变量a和变量b都是A的实例,但是一个指向A的对象,一个指向B对象。
因为B类型是A类型的派生类,可以理解为B类型就是A类型,即B继承A,那么B就是A(但反过来就不正确了),两者之间是一种"是"的关系,所有A b = new B();这种转换没有问题。这里和c++里面的转换关系是一摸一样的(小编还依稀记得大大学学这个知识点的时候,我被叫起来回答了这个问题)
*/
上述代码执行的结果为:
在这里插入图片描述

隐藏方法的注意点:

  • 隐藏方法中是谁的实例就会调用谁的方法,即是基类的实例那么就会调用基类的方法,是派生类的方法则调用派生类的方法。(这里我还要多提一嘴:重写方法中派生类的变量调用派生类重写方法,基类的变量的话,要根据基类引用的是基类的实例还是派生类的实例,如果引用的是基类的实例那么调用基类的方法,如果引用的是派生类的实例则调用派生类的方法。)
  • 隐藏方法可以隐藏非虚方法,还可以隐藏非虚方法的。

好了,说了这么多,我们写一个代码演示一下:抽象方法与虚方法、abstract与virtual、抽象方法与隐藏方法

using System;

namespace _27虚方法_抽象方法_隐藏方法
{
    public abstract class A
    {
        public abstract void hFunction();//抽象方法--抽象方法必须在派生类中重写
    }
    public class B : A
    {
        public override void hFunction()//这里就是实现抽象方法的重写
        {
            Console.WriteLine("B类中重写A类里面的抽象方法hFunction");
        }
        public void jFunction()
        {
            Console.WriteLine("B jFunction");
        }
        public int jFunction(int a)
        {
            Console.WriteLine("{0}", a);
            return a;
        }
        public virtual void sFunction()//virtual---虚方法是已经实现了的,可以被子类覆盖,也可以不覆盖,取决于需求。
        {
            Console.WriteLine("B类的虚方法");
        }
    }
    public class C : B
    {
        public override void sFunction()//函数重写
        {
            Console.WriteLine("C类里面重写了B类的虚方法");
        }
        new public void jFunction()//隐藏方法
        {
            Console.WriteLine("C jFunction");
        }

    }
    public class D : B
    {
        public override void sFunction()//函数重写
        {
            Console.WriteLine("D类里面重写了B类的虚方法");
        }
    }
    class Program
    {
        static void Main(string[] args)
        {
            //B的实例
            B b = new B();

            //A的实例,引用派生类C对象
            B b1 = new C();
            //A的实例,引用派生类D对象
            B b2 = new D();
            /*
这里注意,变量b1和变量b2都是B的实例,但是一个指向C的对象,一个指向D的对象。  
因为C类型是B类型的派生类,可以理解为C类型就是B类型,即C继承B,那么C就是B,所以  B b1 = new C();这种转换没有问题
             */

            //C的实例
            C c = new C();

            //Dog的实例
            D d = new D();

            //重载
            b.jFunction();
            b.jFunction(100);

            //重写和虚方法
            b.sFunction();
            b1.sFunction();
            b2.sFunction();

            //抽象方法  抽象方法--抽象方法必须在派生类中重写
            b.hFunction();

            //隐藏方法  隐藏方法中父类的实例调用父类的方法,子类的实例调用子类的方法。
            b1.jFunction();//父类的实例调用父类的方法---打印:B jFunction
            b2.jFunction();//父类的实例调用父类的方法---打印:B jFunction
            c.jFunction();//子类的实例调用子类的方法---打印:C jFunction
            Console.ReadKey();
        }
    }
}

上面代码的执行结果为:
在这里插入图片描述

抽象类与接口

抽象类:使用关键字abstract修饰的类,抽象类包含抽象方法,抽象方法供派生类去实现。

值得注意的是:

  • 不可以在抽象类外部声明一个抽象方法;
  • 不可以实例化一个抽象类;
  • 抽象类的前面不可以被关键字sealed修饰。(因为sealed修饰的类为密封类,它是不能够被继承的,所以抽象类的前面不能够被sealed修饰)

接口:使用关键字interface声明,接口默认就是public的。

interface IA//声明接口
{
    void Function();
}

接口定义了所有类继承接口时应遵循的语法"合同",接口本身并没有实现任何功能,它只是和声明实现该接口的对象签订一个必须实现哪些行为的契约,即接口告诉你“要求是什么”,派生类要根据接口去完成“要求怎么实现”。

值得注意的是:

  • 接口中不能有字段变量、构造函数,以及不能使用abstract、public、static、new等修饰符修饰;
  • 当一个接口(接口1)继承了其他的接口(接口2),那么继承接口1的类需要实现所有接口的成员;(即接口2里面的成员也要实现)
  • 实现接口时,必须和接口的格式保持一致;
  • 接口内可以定义属性(有get和set的方法)。.

抽象类和接口的区别

  • 抽象类中可以有某些方法的部分实现,接口不能实现方法,接口只是起到一个规范性的作用。抽象类的实例是它的派生类给出的,接口的实例是实现接口的类给出的;
  • 抽象类是类,只能被单继承;而接口却是可以被多继承的,因此,接口也解决了c#里面类可以继承多个基类的问题;
  • 抽象类用于共性,而接口用于规范;
  • 抽象类中的成员可以是私有的、内部的、受保护的,而接口的成员只能被定义为公共的;
  • 如果在抽象类里面添加一个方法,那么继承它的派生类同时也有了这个方法。但在接口中添加一个新方法,这就意味着实现它的类就必须重新编写这个方法。
  • 抽象类中可以有方法,也可以定义非静态的类变量;但接口中只能声明方法、属性、事件、索引器。

(小编也还在学习中,若本文有错误的地方,欢迎指正!)

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

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

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

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

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

抵扣说明:

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

余额充值