如图,page类中实现IHttpHandler中 ProcessRequest 方法时前面加了关键字virtual.
我知道使用virtual可以让子类重写,但仅仅为了能让子类重写吗?麻烦了解的大哥指点下,谢谢源码:https://referencesource.microsoft.com/#System.Web/UI/Page.cs,a7e11608d83dac42

解决方案 »

  1.   

    常用的功能也就是这个吧,子类override
      

  2.   

    当然,从Page派生出来的类,是要重新实现这个方法的,所以必须要virtual
    这和它是不是实现IHttpHandler接口没关系,虽说这个函数是IHttpHandler里面定义的
      

  3.   

    这个叫做NVI模式,目的是依赖倒置https://www.cnblogs.com/keiling/p/3666428.html
      

  4.   

    是的,仅仅是重写。这叫精益求精,让程序员无意间出错的可能性最小。写了virtual,表明是程序员认可,这个方法在派生类中有重写的可能。
    没写virtual时,如果派生类中重写了,编译器会提示一个警告,告诉程序员,你可能重写了父类的方法,这会导致隐藏父类的方法。而写父类的程序员并未声明可以重写(Virtual),所以,你可能是取了和父类中的方法同样的一个名字,请检查。==========
    最新文章:解读经典《C#高级编程》 第四章之 最全泛型协变逆变解读 https://mp.weixin.qq.com/s/zGWKQNw72tM8GQUGXu18JA
    欢迎关注微信公众号 “产品技术知与行” ,解读技术经典书籍(C#,Java,Js),发表技术专题、提供源码下载,打造全面结构化知识库,欢迎对全栈/跨语言技术有兴趣的小伙伴关注。 
      

  5.   

    感觉2楼表达的不是很清晰,是要说加了virtual是为了让子类实现,而不仅仅为了实现IHttpHandler中的方法吗?这样在实现接口时让子类也得到了扩展.
    3楼 NVI模式 确实让我 又涨了见识.
    5楼大哥讲的也很好,提问之前也查看virtual的一些特殊用法,https://blog.csdn.net/songsz123/article/details/7369913,
    这里面说的无virtual和无override的重写就是大哥想说的不严谨的写法吧.
      

  6.   

    偏楼了,如果子类依旧实现接口呢(显示实现接口)那么问题是来了,这不是NVI,也不是(new隐藏和重载),这只是说,我已经实现了,你没必要自己实现。除非你就是非要在显示实现一遍
      

  7.   

    首先要明白,virtual不是abstract,被virtual修饰的方法是有具体实现的。具体到楼主的例子,Page类的ProcessRequest是可以直接调用来实现某些功能的。其次,用virtual来修饰方法,就是给子类提供了复写该方法的可能。
    假如你自定义了一个类 public class CustomPage : Page{},在该类中,你希望ProcessRequest除了父类原本的功能,还有一些自己的私有实现的话,你就需要复写这个方法
    public override void RequestProcess(HttpContext context)
    {
    base.RequestProcess(context);//子类私有代码
    111
    }说白了,被override修饰的方法,既为子类提供了统一的实现,又为子类提供了定制化的可能。
    至于这个方法是不是来自接口,其实关系不大。
      

  8.   

    大家回答那么积极很感谢,根据有些楼主的答案我在本地写了个测试,发现有些不对,代码贴出来供大家指教 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();
                }
            }
        }
    那问题来了,这两种方式哪个比较好些,或者实现这种功能还有更好的方式没,大家都说说自己看法吧