在 C# 中,术语阴影是什么意思?我已阅读此链接,但没有完全理解。
11 回答
阴影隐藏了基类中的方法。使用您链接的问题中的示例:
class A
{
public int Foo(){ return 5;}
public virtual int Bar(){return 5;}
}
class B : A
{
public new int Foo() { return 1;}
public override int Bar() {return 1;}
}
类B
覆盖了虚方法Bar
。它隐藏(阴影)非虚拟方法Foo
。覆盖使用override关键字。阴影是使用new关键字完成的。
在上面的代码中,如果你在定义类中的方法时没有使用new关键字,你会得到这个编译器警告:Foo
B
'test.B.Foo()' hides inherited member 'test.A.Foo()'. Use the new keyword if hiding was intended.
- 覆盖:在基类上重新定义现有方法
- Shadowing:创建一个全新的方法,其签名与基类中的方法相同
假设我有一个实现虚方法的基类:
public class A
{
public virtual void M() { Console.WriteLine("In A.M()."); }
}
我还有一个派生类,它也定义了一个方法 M:
public class B : A
{
// could be either "new" or "override", "new" is default
public void M() { Console.WriteLine("In B.M()."); }
}
现在,假设我写了一个这样的程序:
A alpha = new B(); // it's really a B but I cast it to an A
alpha.M();
对于我希望如何实现它,我有两种不同的选择。new
默认行为是调用 A 的 M 版本。(这与将 " " 关键字应用于 时的行为相同B.M()
。)
当我们有一个名称相同但从基类调用时行为不同的方法时,这称为“阴影”。
或者,我们可以指定 " override
" on B.M()
。在这种情况下,alpha.M()
会调用 B 的 M 版本。
阴影包括在子类中隐藏具有新定义的基类方法。
隐藏和覆盖之间的区别与调用方法的方式有关。
这样,当一个虚方法被覆盖时,基类的方法调用表的调用地址被替换为子例程的地址。
另一方面,当一个方法被隐藏时,一个新的地址被添加到子类的方法调用表中。
当调用相关方法时:
- 获取方法调用表类类型,如果引用基类调用则获取基类方法表,如果有子类引用则获取子类方法表。
- 在表中搜索该方法,如果找到则进行调用,否则搜索基类方法表。
如果我们通过引用子类来调用方法,那么行为是相同的,如果方法被覆盖,方法地址将在基类中找到,如果方法被隐藏,方法地址将在基类中找到子类,由于已经找到,所以不会搜索基类表。
如果我们使用对基类的引用来调用该方法,那么行为就会改变。重写时,由于方法地址覆盖了基类条目,我们将调用子方法,即使持有对基类的引用也是如此。使用阴影,基类方法表(这是唯一可见的,因为我们持有对基类的引用)包含虚拟方法地址,因此将调用基类方法。
一般来说,阴影是一个坏主意,因为它会根据我们对它的引用而对实例的行为产生差异。
扩展肯特的正确答案
在消除何时调用哪个方法的歧义时,我喜欢考虑使用以下内容进行遮蔽与覆盖
- Shadowing:调用的方法取决于调用点的引用类型
- 覆盖:调用的方法取决于调用时对象的类型。
如果要隐藏基类方法,请在基类中使用覆盖[基类中的虚拟方法]
如果要隐藏子类方法,请在基础中使用 new [基础中的非虚拟方法]->shadow
Base B=new Child()
B.VirtualMethod()
-> 调用子类方法
B.NonVirtualMethod()
-> 调用基类方法
覆盖:相同的名称和完全相同的参数,在子类中实现不同。
- 如果被视为 DerivedClass 或 BaseClass,它使用派生方法。
Shadowing : 相同的名称和完全相同的参数,在子类中实现不同。
- 如果被视为 DerivedClass,它使用派生方法。
- 如果被视为 BaseClass,它使用基本方法。
希望这个简短的解释有所帮助。
Shadowing
- 替换父类的完整元素
class InventoryAndSales
{
public int InvoiceNumber { get; set; }
}
//if someone calls for this class then the InvoiceNumber type is now object
class NewInventoryAndSales : InventoryAndSales
{
public new object InvoiceNumber { get; set; }
}
Overriding
- 仅替换实现。它不会替换它不会替换的数据类型,例如您有一个变量,它不会将其转换为方法,因此如果有方法,它将使用该方法并且只更改实现
class InventoryAndSales
{
public virtual int GetTotalSales(int a, int b)
{
return a + b;
}
}
class NewInventoryAndSales : InventoryAndSales
{
//it replaces the implementation in parent class
public override int GetTotalSales(int a, int b)
{
return a * b;
}
}
阴影不是我担心理解或实施的事情,除非它非常“适合”问题。我已经看到它使用不当并且比正确使用更频繁地导致奇怪的逻辑错误。我认为,最大的原因是当程序员忘记将覆盖放在方法签名中时,编译器警告会建议使用 new 关键字。我一直觉得它应该推荐使用覆盖。
private static int x = 10;
static void Main(string[] args)
{ int x = 20;
if (Program.x == 10)
{
Console.WriteLine(Program.x);
}
Console.WriteLine(x);}
输出:
10 20