2010-06-18 36 views
5

我很困惑,为什么new运营商没有按照我的预期工作。C#中的新运营商不覆盖基类成员

注意:以下所有类都在相同的命名空间中定义,并在同一个文件中定义。

这个类允许你用一些提供的文本为写入控制台的任何内容加上前缀。

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看到该实现使用NewClass.consoleWriter变量

我想这是怎样的继承结构在于BaseClass,但该方法是通过

  • 继承的CLR本地执行的方法(在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; 
         } 
        } 
    } 
    
    ... 
    
  • 回答

    8

    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来支持该类。进一步的(正如你在评论中提到的),这只有在你不使这些方法成为静态的时候才有效。

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

    +0

    我无法将静态方法声明为虚拟。也许我应该返回一个类的实例,然后调用非静态方法来解决这个问题? – 2010-06-18 13:06:15

    +0

    是的,那样做。你可以使它成为一个单例,并且仍然提供一个静态的方式来访问它:LogBase.GetLogger(“DataAccess”)。然后你几乎看到它在log4net中的样子,你可能想看看它。 – chiccodoro 2010-06-18 13:39:08

    2

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

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

    +0

    我希望它是静态的,因为我可以在基类中定义我的所有方法,而后者又调用静态成员(在实际实现中它是一个TraceSource)。 因此,我必须改变与我的继承基类有关的唯一一件事是重写的实现。我会更新这个问题。 – 2010-06-18 12:48:29

    1

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

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

    +0

    谢谢Pontus。所有关键词都是为特定目的而选择的,但我现在明白它们是不正确的。原因如下: 'protected'是因为我只想让子类查看成员,以覆盖它。 'new',因为我想要使用重写版本的方法。 作为成员的'静态'和静态方法一起使用(为了方便起见,我不是每次都必须'新'起来)。 但正如我们在这里所描述的,静态成员和方法不是通过继承。 – 2010-06-18 13:45:42