c# - C# 中的新运算符不会覆盖基类成员

标签 c# inheritance new-operator

我很困惑为什么 new 操作符没有像我预期的那样工作。

注意:下面的所有类都在同一个命名空间和同一个文件中定义。

此类允许您使用一些提供的文本为写入控制台的任何内容添加前缀。

public class ConsoleWriter
{
    private string prefix;

    public ConsoleWriter(string prefix)
    {
        this.prefix = prefix;
    }

    public void Write(string text)
    {
        Console.WriteLine(String.Concat(prefix,text));
    }
}

这是一个基类:

public class BaseClass
{
    protected static ConsoleWriter consoleWriter = new ConsoleWriter("");

    public static void Write(string text)
    {
        consoleWriter.Write(text);
    }
}

这是一个实现类:

public class NewClass : BaseClass
{
    protected new static ConsoleWriter consoleWriter = new ConsoleWriter("> ");
}

下面是执行此操作的代码:

class Program
{
    static void Main(string[] args)
    {
        BaseClass.Write("Hello World!");
        NewClass.Write("Hello World!");

        Console.Read();
    }
}

所以我希望输出是

Hello World!
> Hello World!

但是输出是

Hello World!
Hello World!

我不明白为什么会这样。这是我对正在发生的事情的思考过程:

  1. CLR 调用 BaseClass.Write() 方法
  2. CLR 初始化 BaseClass.consoleWriter 成员。
  3. 使用BaseClass.consoleWriter 变量调用并执行该方法

然后

  1. CLR 调用 NewClass.Write()
  2. CLR 初始化 NewClass.consoleWriter 对象。
  3. CLR看到实现在BaseClass,但是方法是通过
  4. 继承的
  5. CLR 使用 NewClass.consoleWriter 变量在本地(在 NewClass 中)执行方法

我以为这就是继承结构的工作原理?

有人可以帮我理解为什么这不起作用吗?

--

更新:

这个场景将按如下方式工作(我是如何实现的)

public class LogBase
{
   protected static TraceSource logger = new TraceSource("");

   public static void Error (string text) { logger.WriteError(text); }
   public static void Info (string text) { logger.WriteInformation(text); }
   public static void Warning (string text) { logger.WriteWarning(text); }
   public static void Verbose (string text) { logger.WriteVerbose(text); }
}

// DataAccess logging
public class DALog : LogBase
{
    protected new static TraceSource logger = new TraceSource("DataAccess");
}

// BusinessObjects logging
public class BOLog : LogBase
{
    protected new static TraceSource logger = new TraceSource("Objects");
}

// BusinessLogic logging
public class BLLog : LogBase
{
    protected new static TraceSource logger = new TraceSource("Logic");
}

// WebUI logging
public class WebUILog : LogBase
{
    protected new static TraceSource logger = new TraceSource("WebUI");
}

原因是我不必为每个类都复制代码。

--

更新(选择解决方案后):

所以为了解决这个问题,我没有使用基类,而是定义了功能类。然后创建新的类,其中包含每个层的 Singleton 实例:

public sealed class LogBase
{
   private TraceSource logger = null;

   public static LogBase GetLogger(string loggerName) 
   {
       return new LogBase(loggerName);
   }

   private LogBase(string loggerName) { logger = new TraceSource(loggerName); }

   public void Error (string text) { logger.WriteError(text); }
   public void Info (string text) { logger.WriteInformation(text); }
   public void Warning (string text) { logger.WriteWarning(text); }
   public void Verbose (string text) { logger.WriteVerbose(text); }
}

// DataAccess logging - no base class
public class DALog 
{
    private static LogBase logger;

    public static LogBase Instance
    { 
         get 
         { 
              if (logger==null) { logger = new TraceSource("DataAccess"); }
              return logger;
         }
    }
}

...

最佳答案

new 运算符实际上并没有在多态性意义上覆盖。它只是添加了另一种“碰巧”具有相同签名但被视为单独方法的方法。仅当您在子类上显式执行该方法时,才会使用"new"实现。

在您的例子中,您只在基类上实现了Write。在那里,您有调用 consoleWriter 的行。 该行引用基类,因此使用了原始实现。它甚至不知道子类中的附加实现。

检查您的代码并按原样看待它:

public class BaseClass
{
    protected static ConsoleWriter consoleWriter = new ConsoleWriter("");

    public static void Write(string text)
    {
        consoleWriter.Write(text);
    }
}


public class NewClass : BaseClass
{
    protected static ConsoleWriter anotherConsoleWriter = new ConsoleWriter(">");
}

您的 BaseClass.Write 永远不会考虑调用 anotherConsoleWriter 而不是 consoleWriter。

如果你想让你的例子工作,你要么需要为 Write 添加一个新的实现:

public class NewClass : BaseClass
{
    protected new static ConsoleWriter consoleWriter = new ConsoleWriter(">");

    public static new void Write(string text)
    {
        consoleWriter.Write(text);
    }
}

... 或者,您最可能最初想要的是,通过使用 override 而不是 new,引入多态性意义上的真正覆盖>。但是,您的基类需要通过将 consoleWriter 声明为 virtual 来支持它。此外(正如您在评论中提到的)这仅在您不将这些方法设为静态时才有效。

您可以申请 Singleton pattern相反,如果你仍然想静态访问你的记录器,或者看看 log4net已经为您解决了所有这些问题。

关于c# - C# 中的新运算符不会覆盖基类成员,我们在Stack Overflow上找到一个类似的问题: https://stackoverflow.com/questions/3069606/

相关文章:

c# - xml.LoadData - 根级别的数据无效。第 1 行,位置 1

javascript - 简单的 javascript 'new' 关键字

java - 从 Java 8 中的 Function 继承的命名具体接口(interface)

C++ operator new 重载,编译错误

c++内存分配错误?

c# - DateTime.TryParse 中的奇怪行为

c# - pInvokeStackImbalance MDA 警告以及如何将其关闭或修复

c# - 如何在 WPF 弹出窗口中放置 close [x]

php - 合并后代对象的数组属性

Java,如何从抽象类继承方法