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

时间:2010-06-18 12:38:14

标签: 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变量
  4. 调用并执行该方法

    然后

    1. CLR调用NewClass.Write()
    2. CLR初始化NewClass.consoleWriter对象。
    3. CLR看到实现位于BaseClass,但该方法是通过
    4. 继承的
    5. CLR使用NewClass变量
    6. 在本地(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;
               }
          }
      }
      
      ...
      

4 个答案:

答案 0 :(得分: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来介绍真正的覆盖多态性。但是,您的基类需要通过将consoleWriter声明为virtual来支持它。此外(正如您在评论中提到的),只有在您不将这些方法设为静态时,此方法才有效。

如果您仍想要对记录器进行静态访问,或者查看已经为您解决了所有这些问题的Singleton pattern,则可以应用log4net

答案 1 :(得分:3)

你遗漏了几个关键点。静态使它成为一个班级领域。受保护意味着它可以从派生类中获得。新意味着您隐藏了基本成员。

你需要这些类是静态的吗?如果不是,您可以将Write方法设为虚拟并在派生类中覆盖它; base.Write(“>”+ text);

答案 2 :(得分:3)

原始问题已被其他人回答:OP期望的行为是覆盖方法的结果,而不是使用new得到的结果。

在OP的回答中,他担心你不能覆盖静态方法。这是真的,但这里有一个来自msdn的博客,它既解释了原因,又讨论了处理它的方法: http://blogs.msdn.com/b/kirillosenkov/archive/2008/02/06/how-to-override-static-methods.aspx

答案 3 :(得分:1)

你知道继承和覆盖适用于(虚拟)实例方法,而不是静态方法,对吗?

您的覆盖为protected,因此在NewClass及其后代之外无法显示。因此,行为是按规格。 new仅允许您创建一个新功能,该功能可在其上下文中使用相同的标识符引用:您的NewClass.Write方法实际上与您的BaseClass.Write无关。