首页 > 解决方案 > c#新手:这段代码没有像我预期的那样工作......为什么?

问题描述

为了更多地了解 C# 中不太常见的结构,我在 codingsight.com 上遇到了这个代码示例:

using System;
using System.Collections.Generic;
using System.Linq;
using System.Text;
using System.Threading.Tasks;

namespace ConsoleApp2
{
    class Program
    {
        static void Main(string[] args)
        {
            MyClassB b = new MyClassB();
            MyClassA a = b;
            a.abc();
            Console.ReadLine();
        }
    }

    class MyClassA
    {
        public MyClassA()
        {
            Console.WriteLine("constructor A");
        }

        public void abc()
        {
            Console.WriteLine("A");
        }
    }

    class MyClassB : MyClassA
    {
        public MyClassB()
        {
            Console.WriteLine("constructor B");
        }

        public void abc()
        {
            Console.WriteLine("B");
        }
    }
}

挑战在于预测输出。在控制台中运行代码会出现:

constructor A
constructor B
A

我打赌我的最后一分钱,输出的第 3 行将是“B”,或“未初始化变量”错误,但实际上它是“A”。为什么?

标签: c#

解决方案


为什么?

因为它是 .NET 语言的一个概念,称为通过继承隐藏。它是这样定义的,微软没有解释为什么要这样做。他们也可以选择任何其他行为,但他们没有。

在评论中你说:

计算机所做的是“某事”,但显然不是代码的意图。

实际上,我们不知道意图是什么。我们需要询问该代码片段的作者。我会说它是有意的,因为这是一个很好的问题,看看有人是否理解了继承的原则。

在标题中,你提到有一个警告——这个警告正是对作者的警告,他没有很好地表达他的意图。或者,作为读者,作者没有充分表达他的意图,这是对你的警告。

警告是

警告 CS0108:“MyClassB.abc()”隐藏了继承的成员“MyClassA.abc()”。如果打算隐藏,请使用 new 关键字。

该警告还告诉我们,作者应该使用new关键字 onMyClassB.abc()明确地告诉读者他明确地使用了“通过继承隐藏”。

现在,有什么替代方案?如果你想继承你所期望的,MyClassA.abc()需要标记为virtual.

但是,仅此更改并不能像您预期的那样修复继承。还有另一个编译器警告

警告 CS0114:“MyClassB.abc()”隐藏了继承的成员“MyClassA.abc()”。要使当前成员覆盖该实现,请添加 override 关键字。否则添加新关键字。

其中明确指出MyClassB.abc()也需要标记为override.

看来你对这个陈述最困惑

MyClassA a = b;

该语句相当于

MyClassA a = (MyClassA) b;

但是由于隐式引用转换,强制转换是多余的:

隐式引用转换是:

[...]

从任何 class_typeS到任何 class_type T,提供S的都是派生自T.

(其中 S 是 MyClassB,T 是 MyClassA)


推荐阅读