Новый оператор в C # не переопределяет член базового класса - PullRequest
6 голосов
/ 18 июня 2010

Я не понимаю, почему оператор 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. 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;
         }
    }
}

...

Ответы [ 4 ]

9 голосов
/ 18 июня 2010

Оператор 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);
    }
}

... или, что вам больше всего нужновероятно, изначально хотел, скорее, ввести real переопределение в смысле полиморфизма, используя override вместо new.Однако ваш базовый класс должен поддерживать это, объявив consoleWriter как virtual.Далее (как вы упомянули в своем комментарии) это работает, только если вы не делаете эти методы статичными.

Вы можете вместо этого применить шаблон Singleton , если вы все еще хотите статический доступ к вашим регистраторамили посмотрите log4net , который уже решил все эти проблемы для вас.

3 голосов
/ 18 июня 2010

На исходный вопрос ответили другие: поведение, которое ожидал OP, это то, что вы получаете от переопределения метода, а не то, что вы получаете от использования new.

В ответах OP он обеспокоен тем, что выне может переопределить статические методы.Это правда, но вот блог от msdn, который объясняет почему и обсуждает способы борьбы с ним: http://blogs.msdn.com/b/kirillosenkov/archive/2008/02/06/how-to-override-static-methods.aspx

3 голосов
/ 18 июня 2010

Вы упустили несколько важных моментов. Статика делает его полем уровня класса.Защищенный означает, что он доступен из производных классов.Новый означает, что вы скрываете базовый член.

Вам нужны эти классы, чтобы быть статичными?Если нет, вы можете сделать метод Write виртуальным и переопределить его в производном классе;base.Write (">" + text);

1 голос
/ 18 июня 2010

Вы знаете, что наследование и переопределение работает для (виртуальных) методов экземпляра, а не статических, верно?

Ваше переопределение protected и поэтому не видно за пределами ваших NewClass и потомков. Поэтому поведение по спец. new позволяет вам создавать только новую функцию, на которую может ссылаться тот же идентификатор в контексте: ваш метод NewClass.Write действительно не имеет ничего общего с вашим BaseClass.Write.

...