最近因为一些原因又开始写C#了,以前一直认为C#就是microsoft版java(这两家已经打了很多年了不要审判我这个脚本小子),现在认真写写还是能发现不少不同的。作为一个备案,还是记录一下编程过程中遇到的一些_feature_,一家之见,不喜勿喷哈。
另:这些天要开始尝试新的文章风格(不是路线),请各位读者海涵。
特色关键字
我们知道在继承与抽象中Java只有表示抽象的abstract和表示继承的extend(接口不算)。
在C#中则有virtual、abstract两个表示抽象的关键字;new和override两个表示覆写的关键字,继承则使用“:”冒号。
这两种方式的高下之分我就不评价了,反正这俩语言也不是只互掐过一次两次- -。
对于所有要覆写的函数,C#都建议使用override表示;而被覆写的变量则推荐使用new表示。
除此之外,virtual关键字为C#中较有特色的一个关键字。关于它的作用我们会用一系列代码来表示。
抽象与继承
首先作为对比我们来写一个Java抽象类(new的方式请不要在意,为了不建工程写的):
public class A{
public static void main(String args[]){
C c=new A().new C();
c.funa();
c.funb();
c.func();
}
public abstract class B{
public String a="B.a";
public abstract void funa();
public void funb(){
System.out.println("B.funb");
}
public void func(){
System.out.println("B.func");
}
}
public class C extends B{
public void funa(){
System.out.println(this.a);
}
public void funb(){
super.funb();
System.out.println("C.funb");
}
}
}
输出为:
可以看到Java采用了很智能的方式,同名方法自动重载且不会调用父类中的方法,无覆盖方法自动调用父类。当类C的一个方法被调用时,若其本身存在同名方法则执行本身的方法,若不存在则向上查找父类的方法并执行。
然后我们可以写一个类似的C#类:
using System;
public class A
{
public abstract class B
{
public string a = "B.a";
public abstract void funa();
public void funb()
{
Console.WriteLine("B.funb");
}
public virtual void func()
{
Console.WriteLine("B.func");
}
public void fund()
{
Console.WriteLine("B.fund");
}
}
public class C : B
{
public override void funa()
{
Console.WriteLine(a);
}
public new void funb()
{
Console.WriteLine("C.funb");
}
public override void func()
{
Console.WriteLine("C.func");
}
}
public static void Main()
{
C c = new C();
c.funa();
c.funb();
c.func();
c.fund();
}
}
输出为:
这里由于中间插了一个virtual方法c导致结果看上去有些不同,但其实除开c方法输出是一样的。单纯从这里看来,也许读者会认为C#和Java的继承除了书写上稍有不同(比如强调你添加override或new,不过其实不添加也是可以过编译的),其它地方也没有很大的差别(override和new我没啥需求就不讲了,它俩也是不大一样的)。
但我们还有一个virtual关键字呢。
Virtual函数
这次我们从父类来执行函数。
Java中其余内容不变,B类的func改成调用funb,main函数中只调用new C().func():
public static void main(String args[]){
C c=new A().new C();
c.func();
}
...
public void func(){
this.funb();
}
再次执行:
可以看到父类和子类的funb都已经执行了。
Java先从C中寻找func->不存在->向父类B寻找func->调用funb->调用B.funb->调用C.funb。
而C#中呢?首先我们需要知道的是C#中不标virtual和abstract的函数是不可以覆写的。所以不会存在普通函数覆写的执行顺序一说。abstract函数没有函数体,只需要看virtual。
同样的将父类中的fund改成调用func,输出如下:
可以看到只执行了子类的virtual函数。事实上,无论C#中有多少覆写关系,最后被调用的永远只会有当前调用函数。例如,上文中的派生类C,如果我们如此调用:
C c = new C();
(B)c.func();
那么被调用的应当是B中的func。而如果没有前面的类型转换,调用的则是C中的func。
Virtual特性的应用
这又要说到笔者为啥要写这篇文章:主要还是因为在实际使用中遇到了virtual函数的好处,也顺带吃了一嘴C#特性的亏- -。
假设我有一个抽象类Abs_selectable,派生出抽象类Abs_componment,再从Abs_componment中派生出两个不同的类Obj_a和Obj_b。
然后存在以下情况:
Abs_componment中有一个监听器函数onTrigger,由于所有子类都需要监听这个事件,所以我在监听器函数中调用一个virtual函数on_trigged,然后将监听器函数本身设置为普通函数。在on_trigged中先做default操作,再在子类中根据不同情况进行覆写,当需要覆写时直接override即可。而对于需要都实现的部分(比如在父类的相同过程上再次扩展)只需要调用一次base.func()即可。在调用时,虽然都调用的是父类的监听函数,但父类的virtual方法会根据情况进行调用,避免多次调用不必要函数的问题。
当然,说这一大堆嘴皮子还是不够形象,我用代码打个比方(这里没有遵循C#推荐的函数书写方式,只是个人习惯原因)。
父类Abs_device,实现virtual方法do_execute用于执行监听器函数:
public abstract class Abs_device : Abs_componment
{
public virtual void do_execute(Mono_ray ray)
{
...
}
public void OnTriggerEnter2D(Collider2D d)
{
if....
then:
this.do_execute(ray);
}
}
子类Device_rotate,不需要实现监听器函数,但在满足前置条件时会调用自己的逻辑do_entering:
public class Device_rotate : Abs_device
{
public override void do_entering(Mono_ray ray)
{
...
}
}
子类,Device_normal,甚至可以什么都不实现,它会自动调用父类的处理方式:
public class Device_rotate : Abs_device {}
这样的多态私以为是更为合理的。
. . . * . * 🌟 * . * . . .
由于很多人问我微信群的事情,所以我建了一个小微信群。现在可以在公众号菜单里选择合作交流->交流群获取交流群二维码,希望大家和谐交流,为更好更友善的行业环境贡献自己的力量。
如果喜欢我的文章,请点赞在看。网安技术文章、安卓逆向、渗透测试、吃瓜报道,尽在我的公众号:
原文始发于微信公众号(重生之成为赛博女保安):Coding漫谈 | 唠唠C#覆写
- 左青龙
- 微信扫一扫
-
- 右白虎
- 微信扫一扫
-
评论