2010-06-18 37 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