2010-11-16 3 views
7
protected static new void WhyIsThisValidCode() 
{ 
} 

Почему вы можете переопределить статические методы? Ничего, кроме ошибок, не может исходить из этого, оно не работает, как вы думаете.Почему главный метод ставится в C#

Пройти следующие занятия.

class BaseLogger 
{ 
    protected static string LogName { get { return null; } } 

    public static void Log(string message) { Logger.Log(message, LogName); } 
} 

class SpecificLogger : BaseLogger 
{ 
    protected static string LogName { get { return "Specific"; } } 
} 

это допускаемые, а код

SpecificLogger.Log("test"); 

является altso допускаемые, но он не делает то, что вы могли бы подумать, глядя на код.

телефонные номера Logger.Log с LogName = null.

Так почему же это разрешено?

+11

Как говорили другие, это не является переопределяющим - и оно делает точно, как я ожидал. Пожалуйста, не думайте, что все думают так же, как вы. –

ответ

24

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

+1

Я иногда пропускаю методы виртуального класса в C#. Но они, вероятно, недостаточно полезны, чтобы гарантировать их добавление к языку. – CodesInChaos

15

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

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

Одна из областей, где статические функции с определенным именем могут быть полезны, - если вы используете отражение и хотите получать информацию о каждом классе, возвращая его из метода. Но, конечно, в большинстве случаев атрибут лучше подходит.

И это, вероятно, не создавать ошибки, так как ваш код производит компилятор предупреждение:

Предупреждения шкура «StaticHiding.SpecificLogger.LogName» наследуется членом «StaticHiding.BaseLogger.LogName». Используйте новое ключевое слово, если было предназначено скрытие.

И если вы используете new, вы должны знать, что делаете.

2

Статические методы и поля не относятся к экземплярам классов, а относятся к определениям классов. Статические методы не играют роли в виртуальном диспетчерском механизме и не являются частью таблицы виртуальных методов.

Это всего лишь методы и поля этого конкретного класса.

Может показаться, что методы и поля «унаследованы», потому что вы можете делать SpecificLogger.Log(), но это просто то, что не позволяет вам постоянно обращаться к базовому классу.

Статические методы и поля на самом деле являются глобальными методами и полями, только вид OO.

0

Вы не переопределяете свойство в базовом классе, а скрываете его. Фактическое свойство, используемое во время выполнения, зависит от того, с каким интерфейсом вы работаете.Следующий пример иллюстрирует:

SpecificLogger a = new SpecificLogger(); 
BaseLogger b = new SpecificLogger(); 

Console.Write(a.Log); // Specific 
Console.Write(b.Log); // null 

В вашем коде метод Log фактически работает против интерфейса BaseLogger - так как метод входа является частью класса BaseLogger.

Статические методы и свойства нельзя переопределить, и если вы хотите скрыть свойство, вы должны использовать ключевое слово new, чтобы обозначить, что вы что-то скрываете.

9

Другие указали, что это не переопределяет, но это все еще оставляет ваш первоначальный вопрос: почему вы в состоянии это сделать? (Но вопрос действительно «почему вы можете скрывать статические методы».)

Это неизбежная функция поддержки независимого управления версиями компонента, который содержит базовые классы и компоненты, которые используют эти базовые классы.

Например, представьте, что компонент CompB содержит базовый класс, а другой компонент CompD содержит производный класс. В версии 1 CompB, возможно, не было никакого свойства, называемого LogName. Автор CompD решает добавить статическое свойство LogName.

Ключевое значение для понимания на данном этапе заключается в том, что автор v1 CompD не собирался заменять или скрывать какую-либо функцию базового класса - в базовом классе не было ни одного элемента, называемого LogName, когда они написали этот код.

Теперь представьте, что выпущена новая версия библиотеки CompB. В этой новой версии автор добавил свойство LogName. Что должно произойти в CompD? Варианты кажутся:

  1. соед больше не работает, потому что он вводит LogName столкновения с LogName добавил к CompB
  2. Как-то сделать LogName в Соед по замене базы CompB LogName. (На самом деле это не совсем ясно, как это может работать со статикой. Однако вы можете предусмотреть это с нестатистикой.)
  3. Рассматривайте два члена LogName как совершенно разные члены, которые имеют одно и то же имя. (На самом деле это не так: они называются BaseLogger.LogName и SpecificLogger.LogName. Но поскольку в C# нам не всегда нужно квалифицировать имя члена с классом, похоже, что они одно и то же.)

.NET решает сделать 3. (и он делает это с как в статике и не-статикой Если вы хотите поведение 2 -. замена - с не-статики, то база должна быть virtualи производный класс должен отметить метод как override, чтобы было ясно, что они сознательно переопределяют метод в базовом классе. C# никогда не сделает метод производного класса заменяет метод базового класса, если только производный класс явно не заявил, что это то, что они хотел.) Это, вероятно, будет безопасным, потому что два члена являются unrelat ed - базовое LogName не существовало даже в том месте, где был введен производный. И это предпочтительнее просто ломать, потому что в последней версии базового класса появился новый член.

Без этой функции в новых версиях .NET Framework невозможно было бы добавить новых членов в существующие базовые классы, не нарушая их.

Вы говорите, что поведение не то, что вы ожидаете. На самом деле это именно то, что я ожидаю, и то, что вы, вероятно, захотите на практике. BaseLogger не знает, что SpecificLogger внедрил собственное свойство LogName. (Нет механизма, с помощью которого он мог бы, потому что вы не можете переопределить статические методы.) И когда автор SpecificLogger написал это свойство LogName, помните, что они писали против v1 BaseLogger, у которого не было логического имени, поэтому они не предполагали, что он также должен заменить базовый метод. Поскольку ни один класс не хочет замены, очевидно, что замена будет неправильной.

Единственный сценарий, в котором вы должны столкнуться в этой ситуации, состоит в том, что два класса находятся в разных компонентах. (Очевидно, вы можете придумать сценарий, когда они находятся в одном компоненте, но почему бы вам это сделать? Если вы владеете обоими частями кода и выпускаете их в одном компоненте, было бы безумно делать это.) И поэтому BaseLogger должен получить свое собственное свойство LogName, что и происходит именно так. Вы, возможно, написал:

SpecificLogger.Log("test"); 

но C# компилятор видит, что SpecificLogger не предоставляет метод журнала, так получается это в:

BaseLogger.Log("test"); 

, потому что это, где определен метод входа.

Таким образом, всякий раз, когда вы определяете метод в производном классе, который не пытается переопределить существующий метод, компилятор C# указывает это в метаданных. (В метаданных метода указано, что этот метод должен быть совершенно новым, не связанным ни с чем в базовом классе.)

Но это дает вам проблему, если вы хотите перекомпилировать CompD. Допустим, у вас есть отчет об ошибке из-за совершенно несвязанного бита кода, и вам нужно выпустить новую версию CompD. Вы скомпилируете его с новой версией CompB. Если код, который вы написали, не был разрешен, вы на самом деле не сможете - старый код, который уже скомпилирован, будет работать, но вы не сможете скомпилировать новые версии этого кода, что было бы немного сумасшедшим ,

Итак, чтобы поддержать этот (откровенно несколько неясный) сценарий, они позволяют вам это делать. Они генерируют предупреждение, чтобы вы знали, что у вас есть именование. Вам нужно поставить ключевое слово new, чтобы избавиться от него.

Это неясный сценарий, но если вы хотите поддерживать наследование по границам компонентов, вам это нужно, иначе добавление новых открытых или защищенных членов в базовый класс неизменно будет изменением. Вот почему это здесь. Но это плохая практика, когда-либо полагаться на нее, следовательно, на то, что вы получаете предупреждение о компиляторе. Использование ключевого слова new, чтобы избавиться от предупреждения, должно быть всегда остановлено.

Суть в том, что эта функция существует только по одной причине, и это должно вывести вас из отверстия в ситуациях, когда новая версия некоторого базового класса добавила элемент, который ранее не существовал, и который сталкивается с членом, который уже находится в вашем производном классе. Если это не та ситуация, в которой вы находитесь, не используйте эту функцию.

(я думаю, что они фактически должны выдавать сообщение об ошибке, а не предупреждение, когда вы выходите из нового, чтобы сделать это более ясным.)

0

для моего удивления следующего кода разрешаются и компилируется без каких-либо ошибок на .NET Framework 4.5.1, VS 2013.

class A 
{ 
    public static void Foo() 
    { 
    } 
} 

class B : A 
{ 

} 

class Program 
{ 
    static void main(string[] args) 
    { 
     B.Foo(); 
    } 
} 
Смежные вопросы