C#用虚函数实现接口中方法意义何在?

忆水思寒 2019-03-12 08:57:37

如图,page类中实现IHttpHandler中 ProcessRequest 方法时前面加了关键字virtual.
我知道使用virtual可以让子类重写,但仅仅为了能让子类重写吗?

麻烦了解的大哥指点下,谢谢

源码:https://referencesource.microsoft.com/#System.Web/UI/Page.cs,a7e11608d83dac42
...全文
294 11 打赏 收藏 转发到动态 举报
写回复
用AI写文章
11 条回复
切换为时间正序
请发表友善的回复…
发表回复
正怒月神 2019-03-13
  • 打赏
  • 举报
回复
就是提供了默认是实现。 子类可以重写。
zhuowp 2019-03-13
  • 打赏
  • 举报
回复
首先要明白,virtual不是abstract,被virtual修饰的方法是有具体实现的。具体到楼主的例子,Page类的ProcessRequest是可以直接调用来实现某些功能的。

其次,用virtual来修饰方法,就是给子类提供了复写该方法的可能。
假如你自定义了一个类 public class CustomPage : Page{},在该类中,你希望ProcessRequest除了父类原本的功能,还有一些自己的私有实现的话,你就需要复写这个方法
public override void RequestProcess(HttpContext context)
{
base.RequestProcess(context);

//子类私有代码
111
}

说白了,被override修饰的方法,既为子类提供了统一的实现,又为子类提供了定制化的可能。
至于这个方法是不是来自接口,其实关系不大。
wanghui0380 2019-03-13
  • 打赏
  • 举报
回复
偏楼了,如果子类依旧实现接口呢(显示实现接口)

那么问题是来了,这不是NVI,也不是(new隐藏和重载),这只是说,我已经实现了,你没必要自己实现。除非你就是非要在显示实现一遍
忆水思寒 2019-03-13
  • 打赏
  • 举报
回复
感觉2楼表达的不是很清晰,是要说加了virtual是为了让子类实现,而不仅仅为了实现IHttpHandler中的方法吗?这样在实现接口时让子类也得到了扩展. 3楼 NVI模式 确实让我 又涨了见识. 5楼大哥讲的也很好,提问之前也查看virtual的一些特殊用法,https://blog.csdn.net/songsz123/article/details/7369913, 这里面说的无virtual和无override的重写就是大哥想说的不严谨的写法吧.
wanghui0380 2019-03-13
  • 打赏
  • 举报
回复
人家的意思很简单
“我把接口需要实现的都实现了,并且还给你留了扩展的口子,你能用我的尽量用我的;如果你觉着看不上我的,也行!自己完全实现那个接口去吧,看不上就别用呗,看不上就自己来”
圣殿骑士18 2019-03-13
  • 打赏
  • 举报
回复
是的,仅仅是重写。这叫精益求精,让程序员无意间出错的可能性最小。

写了virtual,表明是程序员认可,这个方法在派生类中有重写的可能。
没写virtual时,如果派生类中重写了,编译器会提示一个警告,告诉程序员,你可能重写了父类的方法,这会导致隐藏父类的方法。而写父类的程序员并未声明可以重写(Virtual),所以,你可能是取了和父类中的方法同样的一个名字,请检查。

==========
最新文章:解读经典《C#高级编程》 第四章之 最全泛型协变逆变解读 https://mp.weixin.qq.com/s/zGWKQNw72tM8GQUGXu18JA
欢迎关注微信公众号 “产品技术知与行” ,解读技术经典书籍(C#,Java,Js),发表技术专题、提供源码下载,打造全面结构化知识库,欢迎对全栈/跨语言技术有兴趣的小伙伴关注。


忆水思寒 2019-03-13
  • 打赏
  • 举报
回复
大家回答那么积极很感谢,根据有些楼主的答案我在本地写了个测试,发现有些不对,代码贴出来供大家指教

 public interface IEat
    {
        void Eat();
    }

    public class Dog : IEat
    {
        public virtual void Eat()
        {
            Console.WriteLine("Dog eat");
        }
    }

    public class WolfDog : Dog
    {
        public override void Eat()
        {
            Console.WriteLine("WolfDog eat");
        }
    }

    class Tester
    {
        static void Main5(string[] args)
        {
            Dog[] dogs = new Dog[2];
            dogs[0] = new Dog();
            dogs[1] = new WolfDog();
            
            IEat eat;
            for (int i = 0; i < 2; i++)
            {
                eat = dogs[i];
                eat.Eat();
            }
        }
    }
上面是标准的方式,下面改变些 先按照 #5 答主的说法试验下 1 删除关键字 virtual 和子类中override关键字 运行结果: 不是想要的结果 再按照 #8 试验下 显示调用 编译不过 #9 的答主的回答然我想到了,前段时间找 "抽象类和接口 他们什么时候使用比较合适的" 时遇到的一个博客 https://blog.csdn.net/wab719591157/article/details/73741919 里面的需求是,在使用何种方式支付前,有个共同的判断(判断金额不能小于0),博主把这个重复判断的功能在先在基类中实现,然后再定义一个抽象函数,让不同支付行为在继承父类后再实现自己不同的方式,当时看后感觉写的很好,又在这里看到#9 答主答案,我把上面需求用答主的想表达的方式,写了一遍

    public interface PayWay
    {
        bool pay(double money);
    }

    public abstract class AbstractPayWay : PayWay
    {
        private bool verify(double money)
        {
            return money > 0;
        }
        public virtual bool pay(double money)
        {
            bool verify = this.verify(money);
            if (!verify)
            {
                Console.WriteLine("支付金额验证错误!");
                return false;
            }
            return true;
        }

        //public abstract bool doPay();
    }

    public class WeixinPayWay : AbstractPayWay
    {
        public override bool pay(double money)
        {
            if (base.pay(money))
            {
                Console.WriteLine("这里无需校验支付金额,直接调用支付方法就行");
                Console.WriteLine("微信支付成功");
                return true;
            }
            return false;
        }
    }

    public class ZhifubaoPayWay : AbstractPayWay
    {
        public override bool pay(double money)
        {
            if(base.pay(money))
            {
                Console.WriteLine("这里无需校验支付金额,直接调用支付方法就行");
                Console.WriteLine("支付宝支付成功");
                return true;
            }
            return false;
        }
    }
    class Test
    {
        static void Main6(string[] args)
        {
            AbstractPayWay[] AbsPayWay =
            {
                new WeixinPayWay(),
                new ZhifubaoPayWay()
            };
            PayWay payWayByWinXin= AbsPayWay[0];
            payWayByWinXin.pay(111);//微信支付
            PayWay payWayByZhiFuBao = AbsPayWay[1];
            payWayByZhiFuBao.pay(112);//支付宝支付
        }
    }
下面是原博客中用抽象函数写的(我把原Java语言的表达换成了C#)

    public interface PayWay
    {
        bool pay(double money);
    }

    public abstract class AbstractPayWay : PayWay
    {
        private bool verify(double money)
        {
            return money > 0;
        }
        public bool pay(double money)
        {
            bool verify = this.verify(money);
            if (!verify)
            {
                Console.WriteLine("支付金额验证错误!");
                return false;
            }
            return this.doPay();
        }

        public abstract bool doPay();
    }

    public class WeixinPayWay : AbstractPayWay
    {
        public override bool doPay()
        {
            Console.WriteLine("这里无需校验支付金额,直接调用支付方法就行");
            Console.WriteLine("微信支付成功");
            return false;
        }
    }

    public class ZhifubaoPayWay : AbstractPayWay
    {
        public override bool doPay()
        {
            Console.WriteLine("这里无需校验支付金额,直接调用支付方法就行");
            Console.WriteLine("支付宝支付成功");
            return false;
        }
    }

    class Test
    {
        static void Main4(string[] args)
        {
            AbstractPayWay[] AbsPayWay =
            {
                new WeixinPayWay(),
                new ZhifubaoPayWay()
            };
            for (int i = 0; i < 2; i++)
            {
                AbsPayWay[i].doPay();
            }
        }
    }
那问题来了,这两种方式哪个比较好些,或者实现这种功能还有更好的方式没,大家都说说自己看法吧
  • 打赏
  • 举报
回复
“仅仅让子类重写”还不够让你满足吗?
threenewbee 2019-03-12
  • 打赏
  • 举报
回复
这个叫做NVI模式,目的是依赖倒置 https://www.cnblogs.com/keiling/p/3666428.html
stherix 2019-03-12
  • 打赏
  • 举报
回复
当然,从Page派生出来的类,是要重新实现这个方法的,所以必须要virtual 这和它是不是实现IHttpHandler接口没关系,虽说这个函数是IHttpHandler里面定义的
秋的红果实 2019-03-12
  • 打赏
  • 举报
回复
常用的功能也就是这个吧,子类override

110,534

社区成员

发帖
与我相关
我的任务
社区描述
.NET技术 C#
社区管理员
  • C#
  • Web++
  • by_封爱
加入社区
  • 近7日
  • 近30日
  • 至今
社区公告

让您成为最强悍的C#开发者

试试用AI创作助手写篇文章吧