C# 中的 new 运算符不会覆盖基类成员

问题描述 投票:0回答:5

我很困惑为什么

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
    变量在本地执行该方法(在
    NewClass.consoleWriter
    中)

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

请有人帮助我理解为什么这不起作用?

--

更新:

这个场景的工作原理如下(我是如何实现的)

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;
         }
    }
}

...
c# inheritance new-operator
5个回答
9
投票

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
来引入多态意义上的
real
重写。但是,您的基类需要通过将 consoleWriter 声明为
virtual
来支持这一点。此外(正如您在评论中提到的),只有当您不将这些方法设为静态时,这才有效。

如果您仍然希望静态访问记录器,您可以应用 Singleton 模式,或者查看 log4net,它已经为您解决了所有这些问题。


3
投票

你遗漏了一些关键点。静态使其成为一个类级别的字段。受保护意味着它可以从派生类中获得。新意味着您隐藏了基本成员。

您需要这些类是静态的吗?如果没有,您可以将 Write 方法设为虚拟方法并在派生类中重写它; base.Write(">" + 文本);


3
投票

最初的问题已由其他人回答:OP 期望的行为是您通过重写方法获得的行为,而不是通过使用

new
获得的行为。

在OP的回复中,他担心你不能重写静态方法。确实如此,但这里有一篇来自 msdn 的博客,其中解释了原因并讨论了处理方法: 链接


1
投票

您确实知道继承和重写适用于(虚拟)实例方法,而不是静态方法,对吗?

您的覆盖是

protected
,因此在您的
NewClass
和后代之外不可见。因此,该行为是符合规范的。
new
只允许您创建一个新函数,该函数可以在其上下文中通过相同的标识符引用:您的
NewClass.Write
方法实际上与您的
BaseClass.Write
无关。


0
投票

代码的工作多态版本如下所示 -> ConsoleWriter 现在是属性,而不是字段,并且它是虚拟的。 Write 方法不再是静态的。

internal class Program
{
    public static void Main(string[] args)
    {
        var baseWriter = new BaseClass();
        baseWriter.Write("Hello World!");
        
        var newWriter = new NewClass();
        newWriter.Write("Hello World!");
    }
}

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

public class BaseClass
{
    protected virtual ConsoleWriter ConsoleWriter => new ConsoleWriter("");

    public void Write(string text)
    {
        ConsoleWriter.Write(text);
    }
}

public class ConsoleWriter
{
    private string prefix;

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

    public void Write(string text)
    {
        Console.WriteLine(String.Concat(prefix,text));
    }
}
© www.soinside.com 2019 - 2024. All rights reserved.