Другие отметили, что это не имеет преимущественного значения, но это все еще оставляет ваш первоначальный вопрос: почему вы можете это сделать? (Но вопрос действительно в том, «почему вы можете скрыть статические методы».)
Это неизбежная особенность поддержки независимых версий компонентов, которые содержат базовые классы и компоненты, использующие эти базовые классы.
Например, представьте, что компонент CompB содержит базовый класс, а другой компонент CompD содержит производный класс. В версии 1 CompB могло отсутствовать какое-либо свойство с именем LogName. Автор CompD решает добавить статическое свойство с именем LogName.
Важная вещь, которую необходимо понять на этом этапе, заключается в том, что автор v1 CompD не собирался заменять или скрывать какие-либо функции базового класса - в базовом классе не было члена с именем LogName, когда они писали этот код.
Теперь представьте, что выпущена новая версия библиотеки CompB. В этой новой версии автор добавил свойство LogName. Что должно произойти в CompD? Опции выглядят так:
- CompD больше не работает, потому что в LogName возникают конфликты с LogName, добавленным в CompB
- Каким-то образом заставить LogName CompD заменить базовое CompN LogName. (На самом деле не совсем понятно, как это могло бы работать со статиками. Хотя вы могли бы предусмотреть и нестатику.)
- Рассматривайте двух членов LogName как совершенно разных участников, которые имеют одинаковые имена. (На самом деле это не так - они называются BaseLogger.LogName и SpecificLogger.LogName. Но поскольку в C # нам не всегда нужно указывать имя члена класса, похоже, что это одно и то же имя. )
.NET решает сделать 3. (И он делает это как со статикой, так и с нестатикой. Если вам нужно поведение 2 - замена - на нестатику, тогда база должна быть virtual
и производный класс должен пометить метод как override
, чтобы было ясно, что он намеренно переопределяет метод в базовом классе. C # никогда не заставит метод производного класса заменить метод базового класса, если производный класс явно не заявил, что это то, что они хотели.) Вероятно, это будет безопасно, потому что два члена не связаны - базовое LogName даже не существовало в тот момент, когда был введен производный. И это предпочтительнее простого взлома, потому что в последней версии базового класса появился новый член.
Без этой функции для новых версий .NET Framework было бы невозможно добавлять новых членов в существующие базовые классы, не внося при этом существенных изменений.
Вы говорите, что поведение не то, что вы ожидаете. На самом деле это именно то, что я ожидал, и то, что вы, вероятно, хотите на практике. BaseLogger не знает, что SpecificLogger ввел свое собственное свойство LogName. (Нет механизма, с помощью которого он мог бы это сделать, потому что вы не можете переопределить статические методы.) И когда автор SpecificLogger написал это свойство LogName, помните, что они писали против v1 BaseLogger, у которого не было LogName, поэтому они не собирались что он должен заменить базовый метод либо. Поскольку ни один из классов не хочет замены, замена явно была бы неправильной вещью.
Единственный сценарий, в котором вы должны когда-либо оказаться в этой ситуации, заключается в том, что два класса находятся в разных компонентах. (Очевидно, что вы можете придумать сценарий, когда они находятся в одном и том же компоненте, но зачем вам это когда-либо делать? Если вы владеете обоими частями кода и выпускаете их в одном компоненте, было бы безумно когда-либо делать это.) И поэтому BaseLogger должен получить собственное свойство LogName, что и происходит. Вы могли написать:
SpecificLogger.Log("test");
но компилятор C # видит, что SpecificLogger не предоставляет метод Log, поэтому он превращает это в:
BaseLogger.Log("test");
потому что именно там определен метод Log.
Поэтому, когда вы определяете метод в производном классе, который не пытается переопределить существующий метод, компилятор C # указывает это в метаданных. (В метаданных метода есть параметр «newslot», в котором говорится, что этот метод должен быть совершенно новым, не связанным ни с чем в базовом классе.)
Но это создает проблему, если вы хотите перекомпилировать CompD. Допустим, у вас есть сообщение об ошибке из-за какого-то совершенно не связанного фрагмента кода, и вам нужно выпустить новую версию CompD. Вы компилируете это против новой версии CompB. Если код, который вы написали, не был разрешен, вы бы не смогли - старый код, который уже скомпилирован, сработал бы, но вы не смогли бы скомпилировать новые версии этого кода, что было бы немного безумно .
И поэтому, чтобы поддержать этот (откровенно несколько неясный) сценарий, они позволяют вам сделать это. Они генерируют предупреждение, чтобы вы знали, что у вас здесь есть конфликт имен. Вам нужно указать ключевое слово new
, чтобы избавиться от него.
Это непонятный сценарий, но если вы хотите поддерживать наследование через границы компонентов, вам это нужно, иначе добавление новых открытых или защищенных членов в базовый класс неизменно будет критическим изменением. Вот почему это здесь. Но полагаться на это плохая практика, отсюда тот факт, что вы получаете предупреждение компилятора. Использование ключевого слова new
для избавления от предупреждения должно быть только временным ограничением.
Суть заключается в следующем: эта функция существует только по одной причине, и это поможет вам выйти из дыры в ситуациях, когда новая версия некоторого базового класса добавляет члена, которого ранее не было, и который конфликтует с членом, который уже находится в вашем производном классе. Если это не та ситуация, в которой вы находитесь, не используйте эту функцию.
(я думаю, что на самом деле они должны выдавать ошибку, а не предупреждение, когда вы пропускаете новое, чтобы прояснить это.)