正在查看我们代码库中的一些代码,我无法理解它是如何/为什么工作的(并且不会由于无限递归而导致堆栈溢出)。我在下面粘贴了一些等效代码: 我们在类 P1 中定义了一个虚方法 Foo(B),并在类 P2 中重写了它。 P2 还定义了一个私有(private)非虚方法 Foo(A)。 B 派生自 A。P2::Foo(B) 最后有一个调用:Foo(b)。我希望这最终会导致堆栈溢出。 但是,输出是: P2::Foo 虚拟 P2::Foo 私有(private)非虚拟
在这种情况下,覆盖方法中对 Foo 的第二次调用似乎是在选择非虚拟方法 Foo。在 P1 中执行类似操作(取消注释代码)时,我们最终通过递归调用 Foo 无限次。
问题:(终于!) 1. 为什么原始虚方法和重写方法的行为不同?为什么一个调用自己而另一个调用不同的方法? 2. 某处是否指定了优先顺序?请注意,如果我们将 private 修饰符更改为 public,在这两种情况下,我们最终都会调用非虚拟方法(即使我们以这种方式实例化 P2:P1 p2 = new P2(); ,而不是 P2 p2 = new P2( );) 看起来非虚拟版本是首选,除非它在虚拟方法定义中。这是真的吗?
using System;
using System.Collections.Generic;
using System.Linq;
using System.Text;
namespace ConsoleApplication1
{
public class P1
{
static void Main(string[] args)
{
B b = new B();
P2 p2 = new P2();
p2.Foo(b);
// Uncomment code to cause infinite recursion
//P1 p1 = new P1();
//p1.Foo(b);
}
private void Foo(A a)
{
Console.WriteLine("P1::Foo Private Non-Virtual");
}
public virtual void Foo(B b)
{
Console.WriteLine("Inside P1::Foo");
// Uncomment code to cause infinite recursion
// Foo(b);
}
}
public class P2 : P1
{
private void Foo(A a)
{
Console.WriteLine("P2::Foo Private Non-Virtual");
}
public override void Foo(B b)
{
Console.WriteLine("P2::Foo Virtual");
Foo(b);
}
}
public class A
{
public int a = 10;
}
public class B : A
{
public int b = 20;
}
最佳答案
这是因为重载决策仅在无法选择在派生类型上定义的重载时才查看继承的成员。来自规范(第 4 版):
For example, the set of candidates for a method invocation does not include methods marked override (§7.4), and methods in a base class are not candidates if any method in a derived class is applicable (§7.6.5.1).
具体解决您的问题:
Why is the behavior different in the original virtual method and the overridden method?
因为重写方法是在派生类中定义的,并且该类中存在适用的重载,所以不考虑虚方法。不考虑覆盖方法,因为从不考虑覆盖。
Why is one calling itself and the other calling a different method?
派生类中的行为已在上面进行了解释。在基类中,重载决议的最佳候选者是虚方法本身,因为它更具体(B 派生自 A)。
Is there an order of preference specified somewhere?
是的,在 C# Language Specification 中(链接到规范的 Visual Studio 2012 版本的 MSDN 页面)。
Note that if we change the private modifier to public, in both cases, we end up calling the non-virtual method (Even if we instantiate P2 this way: P1 p2 = new P2(); , instead of P2 p2 = new P2();)
在这种情况下,可访问性不是一个重要问题。变量 p2 的类型也不相关,因为您要询问的重载解决方案与虚拟方法的 P2 覆盖中的调用站点有关。虚拟分派(dispatch)确保 Main() 中的调用调用覆盖,而不管变量的静态类型如何。在 P2 的 override void Foo(B b) 的调用点,接收者隐式地是 this,它总是有一个静态类型P2。
It looks like the non-virtual version is preferred, except when it is inside a virtual method definition. Is this true?
不完全是;如上所述,偏好不是针对非虚拟方法,而是针对在接收者类型中定义的方法(即,调用该方法的对象引用的静态类型)。
关于c# - 覆盖 C# 的虚方法 - 为什么这不会导致无限递归?,我们在Stack Overflow上找到一个类似的问题: https://stackoverflow.com/questions/12374981/
我正在学习如何使用Nokogiri,根据这段代码我遇到了一些问题:require'rubygems'require'mechanize'post_agent=WWW::Mechanize.newpost_page=post_agent.get('http://www.vbulletin.org/forum/showthread.php?t=230708')puts"\nabsolutepathwithtbodygivesnil"putspost_page.parser.xpath('/html/body/div/div/div/div/div/table/tbody/tr/td/div
总的来说,我对ruby还比较陌生,我正在为我正在创建的对象编写一些rspec测试用例。许多测试用例都非常基础,我只是想确保正确填充和返回值。我想知道是否有办法使用循环结构来执行此操作。不必为我要测试的每个方法都设置一个assertEquals。例如:describeitem,"TestingtheItem"doit"willhaveanullvaluetostart"doitem=Item.new#HereIcoulddotheitem.name.shouldbe_nil#thenIcoulddoitem.category.shouldbe_nilendend但我想要一些方法来使用
类classAprivatedeffooputs:fooendpublicdefbarputs:barendprivatedefzimputs:zimendprotecteddefdibputs:dibendendA的实例a=A.new测试a.foorescueputs:faila.barrescueputs:faila.zimrescueputs:faila.dibrescueputs:faila.gazrescueputs:fail测试输出failbarfailfailfail.发送测试[:foo,:bar,:zim,:dib,:gaz].each{|m|a.send(m)resc
我正在尝试设置一个puppet节点,但rubygems似乎不正常。如果我通过它自己的二进制文件(/usr/lib/ruby/gems/1.8/gems/facter-1.5.8/bin/facter)在cli上运行facter,它工作正常,但如果我通过由rubygems(/usr/bin/facter)安装的二进制文件,它抛出:/usr/lib/ruby/1.8/facter/uptime.rb:11:undefinedmethod`get_uptime'forFacter::Util::Uptime:Module(NoMethodError)from/usr/lib/ruby
我有一个模型:classItem项目有一个属性“商店”基于存储的值,我希望Item对象对特定方法具有不同的行为。Rails中是否有针对此的通用设计模式?如果方法中没有大的if-else语句,这是如何干净利落地完成的? 最佳答案 通常通过Single-TableInheritance. 关于ruby-on-rails-Rails-子类化模型的设计模式是什么?,我们在StackOverflow上找到一个类似的问题: https://stackoverflow.co
我想了解Ruby方法methods()是如何工作的。我尝试使用“ruby方法”在Google上搜索,但这不是我需要的。我也看过ruby-doc.org,但我没有找到这种方法。你能详细解释一下它是如何工作的或者给我一个链接吗?更新我用methods()方法做了实验,得到了这样的结果:'labrat'代码classFirstdeffirst_instance_mymethodenddefself.first_class_mymethodendendclassSecond使用类#returnsavailablemethodslistforclassandancestorsputsSeco
我正在使用的第三方API的文档状态:"[O]urAPIonlyacceptspaddedBase64encodedstrings."什么是“填充的Base64编码字符串”以及如何在Ruby中生成它们。下面的代码是我第一次尝试创建转换为Base64的JSON格式数据。xa=Base64.encode64(a.to_json) 最佳答案 他们说的padding其实就是Base64本身的一部分。它是末尾的“=”和“==”。Base64将3个字节的数据包编码为4个编码字符。所以如果你的输入数据有长度n和n%3=1=>"=="末尾用于填充n%
我主要使用Ruby来执行此操作,但到目前为止我的攻击计划如下:使用gemsrdf、rdf-rdfa和rdf-microdata或mida来解析给定任何URI的数据。我认为最好映射到像schema.org这样的统一模式,例如使用这个yaml文件,它试图描述数据词汇表和opengraph到schema.org之间的转换:#SchemaXtoschema.orgconversion#data-vocabularyDV:name:namestreet-address:streetAddressregion:addressRegionlocality:addressLocalityphoto:i
为什么4.1%2返回0.0999999999999996?但是4.2%2==0.2。 最佳答案 参见此处:WhatEveryProgrammerShouldKnowAboutFloating-PointArithmetic实数是无限的。计算机使用的位数有限(今天是32位、64位)。因此计算机进行的浮点运算不能代表所有的实数。0.1是这些数字之一。请注意,这不是与Ruby相关的问题,而是与所有编程语言相关的问题,因为它来自计算机表示实数的方式。 关于ruby-为什么4.1%2使用Ruby返
我在我的项目中添加了一个系统来重置用户密码并通过电子邮件将密码发送给他,以防他忘记密码。昨天它运行良好(当我实现它时)。当我今天尝试启动服务器时,出现以下错误。=>BootingWEBrick=>Rails3.2.1applicationstartingindevelopmentonhttp://0.0.0.0:3000=>Callwith-dtodetach=>Ctrl-CtoshutdownserverExiting/Users/vinayshenoy/.rvm/gems/ruby-1.9.3-p0/gems/actionmailer-3.2.1/lib/action_mailer