Что такое статический класс

Статические классы
В этой статье я хочу рассказать о создании статических классов (не просто классов, в которых всего лишь присутствуют статические сущности, такие как поля, метод, свойства и т.п., об этом я рассказывал в предыдущей статье), а так же об особенностях их использования. Класс считается статическим, если при его создании, перед ключевым словом class указывается ключевое слово static. Определение класса статическим, означает то, что этот класс является набором только статических сущностей (полей, методов, свойств и т.п.), и, внимание, создавать объекты такого класс нельзя!
Кроме этого, на статический класс, накладываются как минимум следующие ограничения:
- статический класс не может служить базовым для других классов, а так же, сам не может быть наследником какого-либо произвольного класса;
- статический класс не может содержать сущности с атрибутами доступа protected и protected internal (да они и не нужны, все равно наследование запрещено);
- класс не может иметь перегруженных операций.
О перегрузке операций (или операторов, например сложения, вычитания и т.п.) я напишу отдельную статью, или даже цикл статей…
А сейчас я покажу пример создания статического класса. Но для этого, давайте вспомним пример из предыдущей статьи. В ней был рассмотрен класс «Operations», который выглядел так:
//Класс, реализующий некие операции class Operations < //Статический метод, принимающий два целых числа и возвращающий их сумму public static int Sum(int aFirstArg, int aSecondArg) < return aFirstArg + aSecondArg; >>
Этот класс не имеет никаких не статических методов, полей свойств и прочего, и он отлично подходит для того, чтобы сделать его статическим. Ведь действительно, создавать объекты этого класса практически бессмысленно! Вот так будет выглядеть этот класс, если мы сделаем его статическим:
//Класс, реализующий некие операции static class Operations < //Статический метод, принимающий два целых числа и возвращающий их сумму public static int Sum(int aFirstArg, int aSecondArg) < return aFirstArg + aSecondArg; >>
Обратите внимание, даже не смотря на то что весь класс является статическим, метод «Sum» мы тоже явно сделали статическим! Так требует компилятор…
Вот так вот у меня для Вас информация о статических классах.
Добавить комментарий Отменить ответ
Для отправки комментария вам необходимо авторизоваться.
Статические классы
Существуют классы, не предназначенные для создания экземпляров, например Console, Math, Environment и ThreadPool. У этих классов есть только статические методы. В сущности, такие классы существуют лишь для группировки логически связанных членов.
Компилятор налагает на статический класс ряд ограничений:
- Класс должен быть прямым потомком System.Object — наследование любому другому базовому классу лишено смысла, поскольку наследование применимо только к объектам, а создать экземпляр статического класса невозможно
- Класс не должен реализовывать никаких интерфейсов, поскольку методы интерфейса можно вызывать только через экземпляры класса
- В классе можно определять только статические члены (поля, методы, свойства и события). Любые экземплярные члены вызовут ошибку компиляции.
- Класс нельзя использовать в качестве поля, параметра метода или локальной переменной, поскольку это подразумевает существование переменной, ссылающейся на экземпляр, что запрещено.
Использовав ключевое слово static для класса, компилятор C# автоматически делает его абстрактым и запечатанным (sealed), что логично т.к экземпляры такого класса создавать нельзя, ровно как и наследование.
results matching » «
No results matching » «
Зачем нужен static class?
Статические переменные нужны для доступа к ним, без создания экземпляра класса. А вот зачем нужен static класс?
Отслеживать
122k 24 24 золотых знака 124 124 серебряных знака 297 297 бронзовых знаков
задан 18 ноя 2016 в 9:31
2,188 2 2 золотых знака 15 15 серебряных знаков 36 36 бронзовых знаков
4 ответа 4
Сортировка: Сброс на вариант по умолчанию
Статическим классом в java может быть только вложенный класс. Если класс отмечен как static, то он ведет себя, как обычный класс.
например, есть класс А, вложенный статический класс B и вложенный (нестатический) класс С:
public class A < . static public class B< >public class C < >>
и мы хотим создать экземпляры этих классов во «внешнем» коде
public class Test < public static void main(String[] args) < A a = new A(); // обычный класс A.B b = new A.B(); // статический вложенный класс A.C c = a.new C(); // вложенный класс, связан с экземпляром А // A.C c = new A.C(); // синтаксическая ошибка (не скомпилится) >>
или внутри статических методов класса А
public class A < . static public class B< >public class C < >public static void main(String[] args) < A a = new A(); // обычный класс A.B b = new A.B(); // статический вложенный класс A.C c = a.new C(); // вложенный класс, связан с экземпляром А // A.C c = new A.C(); // синтаксическая ошибка (не скомпилится) >public static void test() < A a = new A(); // обычный класс A.B b = new A.B(); // статический вложенный класс A.C c = a.new C(); // вложенный класс, связан с экземпляром А // A.C c = new A.C(); // синтаксическая ошибка (не скомпилится) >>
На мой взгляд использование статического класса может быть уместно, как небольшой класс, который по смыслу тесно связан с «основным» внешним классом.
public class Tree < static public class Node< >>
В этой ситуации так же можно вынести вложенный класс в обычный и переместить оба класса в отдельный package.
Единственным отличием вложенного статического класса от обычного, которое мне видится, — это более снисходительное отношение к видимости методов и полей между вложенным классом и его внешним классом.
public class A < private void privateMethod()< B b = new B(); b.privateMethod(); // есть доступ к приватным методам/полям >static public class B < private void privateMethod()< A a = new A(); a.privateMethod(); // есть доступ к приватным методам/полям >> >
NEWOBJ.ru → Введение в ООП с примерами на C# →
Статический метод ( static ) класса – метод, не имеющий доступа к состоянию (полям) объекта, то есть к переменной this .
Для объявления статического метода используется ключевое слово static :
private static float SquareGeron (float a, float b, float c) < /* … */ >
Статический метод может быть вызван как через экземпляр класса, так и через имя класса. Например, из методов класса Triangle мы можем обратиться к статическому методу SquareGeron следующими способами:
class Triangle < public void SomeMethod () < // 1 - Неявно через объект, то есть через переменную this. SquareGeron (1,2,2); // 2 - Явно через объект, то есть через переменную this. this.SquareGeron (1,2,2); // 3 - Через имя класса, без использования объекта. Square.SquareGeron (1,2,2); >>
Аналогично, извне класса, при условии, что мы сделаем метод SquareGeron открытым:
Square sq = new Square(); // 2 - Через объект. sq.SquareGeron (1,2,2); // 3 - Через имя класса. Square.SquareGeron(1,2,3);
Подчеркнем, что во всех перечисленных случаях метод не имеет доступа к переменной this , то есть даже при вызове через экземпляр, ему не передается, как для обычных методов, параметр this , он не знает, какой именно объект его вызвал и, соответственно, у него нет доступа к полям объекта. Также, как следствие, из статического метода нельзя вызвать нестатический метод того же класса для вызванного объекта. При этом, статический метод, как и любой другой метод класса, имеет доступ к полям и методам объектов этого класса независимо от их уровня видимости:
class Vector < private float x; private float y; public Vector() < >private static float Length (float x0, float y0, float x1, float у1) < /* . */ >public float Length () < // Из нестатического метода можно вызывать статический метод. return Length (0, 0, x, y); >public static Vector Sum (Vector a, Vector b) < Vector sum = new Vector(); // Статические метод, как и нестатический, // имеет доступ к полям, в том числе закрытым (private) // объектов того же класса: sum.x = a.x + b.x; sum.y = a.y + b.y; return sum; >>
Слово «статический» используется в том смысле, что статические методы не относятся к динамике объекта, не используют и не меняют его состояния.
Вспомним, что мы уже использовали статический метод для вычисления квадратного корня Math.Sqrt . Класс System.Math реализует в виде статических методов и другие математические функции. Однако есть множество причин, почему в большинстве случаев следует избегать использования статических методов.
Программисты, не имеющие опыта ООП, часто начинают широко использовать статические методы как способ программировать на объектно-ориентированном языке в процедурном стиле. Действительно, для статических методов их класс – лишь способ синтаксической группировки. Более того, использование статических методов – это всегда в некотором смысле отход от ООП, так как он делает невозможным использование всех ключевых элементов объектно-ориентированного программирования: абстрактных типов данных, наследования, полиморфизма. Сформулируем следующее правило: в первом приближении статическими следует делать только 1) небольшие 2) вспомогательные 3) закрытые ( private ) методы класса. Практически всегда методы, не удовлетворяющие приведенному правилу и не обращающиеся к полям объекта, можно и нужно вынести в отдельный класс. Например, если бы метод SquareGeron был большим, то следовало бы создать класс SquareGeronCalculator и создать там открытый метод Calc .
§ 25. Статические поля. Аналогично тому, как статический метод не привязан к объекту, мы можем объявить статическое поле, не являющееся частью никакого объекта, которое будет создаваться в одном экземпляре и будет доступно из любого объекта класса или через имя класса.
Например, следующий код считает число объектов типа Point , созданных с момента запуска приложения:
class Point < private static long newCount; public Point() < // Эквивалентно newCount++, но корректно работает в многопоточном окружении. Interlocked.Increment(ref newCount); >>
Экземпляр статической переменной создается автоматически до первого ее использования (когда именно – не регламентируется), а при создании экземпляров класса память для статических переменных не выделяется. Таким образом, в приведенном примере в некоторый момент времени после запуска приложения и до вызова команды увеличения значения newCount будет создан ровно один экземпляр этой переменной, а каждый создаваемый объект Point будет увеличивать ее значение в своем конструкторе.
Применительно к статическим полям также можно сформулировать правило: в первом приближении следует избегать использовать статические полей.
Причин тут много, в частности, создаются неявные связи между объектами одного класса. Также статические переменные не обрабатываются сборщиком мусора и существуют до закрытия программы, поэтому в крупных приложениях статические поля могут «связать» ощутимый объем памяти, освобождаемый только при закрытии программы.
Тем не менее, статические поля, как и статические методы, используются достаточно широко. Рассмотрим следующий типичный пример.
Положим, мы хотим реализовать авторизацию пользователей, то есть управлять доступностью функциональных возможностей приложения в зависимости от роли текущего пользователя. Для этого в приложении фиксируется перечень возможностей ( features ) и при вызове соответствующих методов, выполняется проверка, разрешен ли текущему пользователю доступ к запрашиваемой функциональной возможности. В первом приближении мы могли бы задать уникальное название для каждой функциональной возможности и привязывать к текущему пользователю перечень строк – список разрешенных возможностей. Тогда авторизация могла бы выглядеть следующим образом:
public class ACL // access control list < // Список возможностей, доступных текущему пользователю. private string[] currentUserAllowedFeatures; public void Authorize (string feature) < // Если у пользователь нет запрашиваемой возможности… if (!currentUserAllowedFeatures.Contains (feature)) // …формируем исключение. throw new Exception (); >> // Использование: public class SomeClass < private ACL acl; // Объект acl создается и инициализируется // где-то при запуске программы. public SomeClass (ACL acl) < this.acl = acl; >public void DoSomeJob () < // Сначала проверяем, если ли у текущего пользователя доступ // к указанной возможноти (ADMINISTRATOR). // Если доступа нет, исключение прервет выполнение метода. acl.Authorize (“ADMINISTRATOR”); // . >>
Проблема этого решения в том, что легко допустить ошибку в написании названия функциональной возможности. Однако мы можем перечислить допустимые названия как открытые неизменяемые статические поля некоторого класса:
public Feature < public static readonly string Administrator = “ADMINISTRATOR”; public static readonly string Guest = “GUEST”; // . >// Фрагмент из предыдущего листинга: public void DoSomeJob ()
Такое решение лучше, но оно все еще позволяет нам передать в метод Authorize произвольную строку, не используя класс Feature . Проанализируйте следующий код:
public Feature < private string code; private Feature (string code) < this.code = code; >public string GetCode() < return code; >public static readonly Feature Administrator = new Feature (“ADMINISTRATOR); public static readonly Feature Guest = new Feature (“GUEST”); // . > //class ACL public void Authorize (Feature feature) < if (!currentUserAllowedFeatures.Contains (feature.GetCode())) throw new Exception (); >// class SomeClass public void DoSomeJob ()
Теперь экземпляры класса Feature представляют отдельные функциональные возможности, но так как единственный конструктор этого класса объявлен как закрытый ( private ), то они не могут быть созданы извне класса. В статических открытых полях сохраняем фиксированный перечень экземпляров этого же класса. Таким образом, в метод Authorize мы передаем объект типа Feature , но не можем создать его самостоятельно, а используем перечень фиксированных, «зашитых» в классе Feature объектов.
Мы можем пойти еще дальше, сохраняя в классе ACL не массив строк, а массив объектов Features :
class ACL < private Feature[] currentUserAllowedFeatures; public void Authorize (Feature feature) < for (int i = 0; i < currentUserAllowedFeatures.Length; i++) < if (currentUserAllowedFeatures[i].GetCode() == feature.GetCode()) return; >throw new Exception (); > >
Такое решение делает безопасным не только передачу параметра в Authorize , но и формирование списка currentUserAllowedFeatures , который теперь также гарантированно не будет содержать произвольных строк.
В заключение, повторимся, что хотя механизм статических полей и методов широко применяется в современной практике объектно-ориентированного программирования, использовать эти возможности следует крайне осмотрительно. Дополнительные аргументы против статических методов и полей мы рассмотрим в последующих главах.
Вопросы и задания
Что такое статические методы, статические поля?
Верно ли говорить о статических классах или статических объектов? В чем их отличие (если верно) от неизменяемых классов и объектов?
Статический метод не имеет доступ к полям объекта, но имеет ли он доступ к полям объекта того же класса, переданного в параметрах этого метода?
В каком порядке следует использовать ключевые слова public/private и static ?
Сравните следующие поля класса:
public class SomeClass < static int field1 = 1; const int field2 = 2; readonly int field3 = 3; static readonly int field4 = 4; int field5 = 5; >
Когда происходит выделение памяти, создаются ли экземпляры полей для каждого экземпляра объекта, возможно ли изменение значений полей? Какие еще есть отличия между ними?
Статические поля и методы часто применяются для реализации объектов-одиночек 35 . Объект-одиночка ( singleton ) – объект, который должен существовать в программе в одном экземпляре. К примеру, это может быть объект, хранящий глобальные параметры приложения. Разберите следующие реализации:
// 1 вариант public class Settings < public string TempWorkspacePath() < /* . */ >public static Settings Singleton = new Settings(); > // Использование: string path = Settings.Singleton.TempWorkspacePath(); // 2 вариант public class Settings < public string TempWorkspacePath() < /* . */ >private static Settings singleton; private Settings () < singleton = new Settings(); >public Settings Get () < if (singleton == null) singleton = new Settings(); return singleton; >> // Использование: string path = Settings.Get().TempWorkspacePath();
Объясните, почему вторая реализация лучше? Почему во втором варианте используется закрытый ( private ) конструктор? Почему не используется ключевое слово readonly для поля singleton ?
* Объясните почему использование newCount++ , вместо метода Interlocked.Increment может привести к ошибкам (неверном подсчету).
* Изучите механизм перечислений в C# ( enum ).
- 35. Отметим попутно, что шаблон «одиночка» в современной практике часто стремятся заменять на шаблон «инверсии управления».↩︎