C # статический член "наследование" - почему это вообще существует? - PullRequest
44 голосов
/ 17 февраля 2010

В 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() (что, вероятно, упрощает вещи для ВМ), не говоря уже о том, чтобы написать другую реализацию 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" ...

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

Ответы [ 7 ]

25 голосов
/ 18 февраля 2010

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

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

Я думаю, что Мартин Фаулер в своей работе над 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();
    }
}

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

public class AnotherThing : Thing { }

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

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

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

10 голосов
/ 18 февраля 2010

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

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

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

8 голосов
/ 17 февраля 2010

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

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

5 голосов
/ 18 февраля 2010

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

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

0 голосов
/ 18 февраля 2010

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

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

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

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

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

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

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

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

0 голосов
/ 17 февраля 2010

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

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

class Subclass : Base
{
   public void Run()
    {
       Helper("From the subclass");
    }
}
0 голосов
/ 17 февраля 2010

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

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

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

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

...