Подтвердить что ты не робот

С# static member "inheritance" - почему это вообще существует?

В С# статические члены суперкласса "наследуются" в области подклассов. Например:

class A { public static int M() { return 1; } }
class B : A {}
class C : A { public new static int M() { return 2; } }
[...]
A.M(); //returns 1
B.M(); //returns 1 - this is equivalent to A.M()
C.M(); //returns 2 - this is not equivalent to A.M()

Теперь вы не можете наследовать статические классы, и единственное место, где я могу себе представить, что статическое наследование может иметь значение, полностью игнорирует его: хотя вы можете сделать общее ограничение, для которого параметр типа T должен быть подклассом A, вы по-прежнему не можете вызвать T.M() (что, вероятно, упрощает работу для виртуальной машины), не говоря уже о написании другой реализации M в подклассе и использовать ее.

Итак, "наследование" статических членов просто выглядит как загрязнение пространства имен; даже если вы явно квалифицируете имя (т.е. B.M) A, версия по-прежнему разрешена.

Изменить сравнить с пространствами имен:

namespace N1{  class X();   }
namespace N1.N2 {  class X();   }
namespace N1.N2.N3 { [...] }

Внутри N1.N2.N3 Имеет смысл, что если я использую X без квалификации, это относится к N1.N2.X. Но если я явно ссылаюсь на N1.N2.N3.X - и такой класс не существует, я не ожидаю, что он найдет версию N2; и, действительно, компилятор сообщает об ошибке, если вы попробуете это. В отличие от этого, если я явно ссылаюсь на B.M(), почему компилятор не сообщает об ошибке? В конце концов, нет метода "М" в "В"...

Какова цель этого наследования? Можно ли конструктивно использовать эту функцию?

4b9b3361

Ответ 1

Итак, "наследование" статического члены просто выглядят как пространство имен Загрязнение

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

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

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

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

public class Thing
{
    // typical per-instance stuff
    int _member1;
    protected virtual void Foo() { ... }
    public void Bar() { ... }

    // factory method
    public static Thing Make()
    {
        return new Thing();
    }
}

Это статический шаблон метода factory. Это бессмысленно большую часть времени, но еще хуже то, что теперь у нас есть это:

public class AnotherThing : Thing { }

Теперь у него есть статический метод Make, который возвращает Thing, а не AnotherThing.

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

Но это только одна из тех слишком поздних вещей. У всех реальных рабочих языков (и библиотек и продуктов) есть несколько из них. С# очень мало.

Ответ 2

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

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

Конечно, они не наследуются, это просто ярлык

Ответ 3

То, как это работает, вероятно, будет просто глупым ответом в большинстве случаев. Но в этом случае так оно и работает; поскольку вы получаете от A, вы говорите, что вы добавили дополнительные функции A +.

Следовательно, вам нужно иметь доступ к тем же переменным, что и через экземпляр A.

Однако наследование статического класса не имеет никакого смысла, в то время как доступ к статическим членам/полям/методам делает.

Примером этого является следующее:

internal class BaseUser
{
    public static string DefaultUserPool { get; set; }
}
internal class User : BaseUser
{
    public int Id { get; set; }
    public string Name { get; set; }
    public User Parent { get; set; }
}

Где тест выглядит следующим образом:

User.DefaultUserPool = "Test";
BaseUser.DefaultUserPool = "Second Test";

Console.WriteLine(User.DefaultUserPool);
Console.WriteLine(BaseUser.DefaultUserPool);

Оба из WriteLines выходят "Второй тест", это связано с тем, что и BaseUser, и User должны использовать DefaultUserPool, по дизайну. И переопределение статических реализованных методов не создавало бы смысла mucn, поскольку это просто аксессор в дочернем классе.

Может быть только один. Переопределение этого означает, что для этого подкласса существует новая реализация, которая убьет термин "статический".

Ответ 4

На самом деле, как я понимаю, это просто ярлык, предоставленный компилятором. Синтаксис сахара. B.M() будет компилироваться только в A.M(), так как B не имеет static M() и A делает. Это для упрощения написания, ничего другого. Нет "статического наследования".

Добавлено: И требование для new, когда "переопределение" просто так, что вы случайно не стреляете в ногу.

Ответ 5

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

игнорировать вышеизложенное по какой-то причине я думал о запечатанном вместо статического

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

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

Ответ 6

Я думаю, что для доступа к защищенным статическим членам базового класса.

class Base
{
    protected static void Helper(string s)
    {
       Console.WriteLine(s);
    }
}

class Subclass : Base
{
   public void Run()
    {
       Helper("From the subclass");
    }
}

Ответ 7

Итак... Какая альтернатива?

Вопрос упоминается...

Почему компилятор не сообщает об ошибке? В конце концов, нет метода "М" в "В"...

Но там есть производный метод "М" в классе "В".

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

Википедия...

Политизм подтипов, почти универсально называемый просто полиморфизмом в контексте объектно-ориентированного программирования, - это способность одного типа, A, появляться и использоваться как другой тип, B....

В сильно типизированных языках полиморфизм обычно означает, что тип A каким-то образом происходит от типа B, или тип C реализует интерфейс, который представляет тип B.