Статическое членское “наследование” C# - почему это существует вообще?

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

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() (который, вероятно, упрощает вещи для VM), уже не говоря о записи другое 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(), почему компилятор не сообщает об ошибке? В конце концов, в "B" нет никакого "M" метода...

Какую цель это наследование имеет? Эта функция может быть использована конструктивно так или иначе?

45
задан Eamon Nerbonne 18 February 2010 в 13:14
поделиться

7 ответов

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

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

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

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

Особенно ужасно скрывать частные статические изменяемые данные внутри реализации класса «instancey». Но есть еще статические методы, которые еще хуже микшеры.Вот типичное использование статических методов, смешанных в классе:

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();
    }
}

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

public class AnotherThing : Thing { }

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

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

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

25
ответ дан 26 November 2019 в 21:28
поделиться

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

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

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

10
ответ дан 26 November 2019 в 21:28
поделиться

Вот как это работает , вероятно, в большинстве случаев будет просто глупым ответом. Но в данном случае это работает так; поскольку вы производите от 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, и пользователь должны использовать DefaultUserPool, по замыслу . А переопределение статических реализованных методов не имело бы большого смысла, поскольку это всего лишь метод доступа в дочернем классе.

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

8
ответ дан 26 November 2019 в 21:28
поделиться

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

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

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

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

0
ответ дан 26 November 2019 в 21:28
поделиться

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

 java -Dswing.defaultlaf=com.sun.java.swing.plaf.windows.WindowsLookAndFeel YourApp

Если применяется правильный внешний вид, то перед созданием диалогового окна JFileChooser можно добавить код внешнего вида в программу. Можно сказать, что простая программа будет выглядеть следующим образом:

 public static void main(String[] args) {
try {

    UIManager.setLookAndFeel("com.sun.java.swing.plaf.windows.WindowsLookAndFeel");

} 
catch (Exception e) {
   // handle exception
}

JFileChooser chooser = new JFileChooser(); 
//etc
}
-121--335522-
-(Bar*) makeBar
{
    return [[[Bar alloc] init] autorelease];
}

2-й случай является предпочтительным способом возврата объекта Objective-C. Кроме + alloc , -копировать... и -создать... , метод не должен сохранять права собственности на возвращаемый объект, т.е. (изменение) количество удержаний должно быть равно 0.

Однако [[Bar alloc] init] делает объект имеющим значение retainCount, равное + 1, таким образом, он должен быть освобожден перед возвращением. Но -отключение немедленно освобождает объект, делая метод бесполезным. Вот почему используется -autorelease - это отложенное -разрешение , т.е. объект будет выпущен в конечном итоге, но не сейчас, так что другие части кода все еще могут взаимодействовать с ним, но счет удержаний все еще может быть сбалансирован до 0.


Bar *b = [[self makeBar] retain];

Вы должны не сохранять его , если вы не хотите быть долгосрочным владельцем объекта b .

-121--3926362-

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

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

class Subclass : Base
{
   public void Run()
    {
       Helper("From the subclass");
    }
}
0
ответ дан 26 November 2019 в 21:28
поделиться

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

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

почему компилятор не сообщает об ошибке? В конце концов, в «B» нет метода «M» ...

Но есть производный метод «M» в «B» класс.

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

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

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

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

0
ответ дан 26 November 2019 в 21:28
поделиться

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

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

5
ответ дан 26 November 2019 в 21:28
поделиться
Другие вопросы по тегам:

Похожие вопросы: