Что такое статический класс
Перейти к содержимому

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

  • автор:

Статические классы и члены статических классов (Руководство по программированию в C#)

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

UtilityClass.MethodA(); 

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

double dub = -3.14; Console.WriteLine(Math.Abs(dub)); Console.WriteLine(Math.Floor(dub)); Console.WriteLine(Math.Round(Math.Abs(dub))); // Output: // 3.14 // -4 // 3 

Как и в случае со всеми типами классов, среда выполнения .NET загружает сведения о типе статического класса при загрузке программы, ссылающейся на класс. Программа не может точно указать, когда загружается класс. Однако он гарантированно загружает и инициализирует поля и вызывается статическим конструктором до того, как класс ссылается впервые в вашей программе. Статический конструктор вызывается только один раз, и статический класс остается в памяти на время существования домена приложения, в котором находится программа.

Создание нестатического класса, который допускает создание только одного экземпляра самого себя, см. в документе Реализация Singleton в C#.

Ниже приведены основные возможности статического класса.

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

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

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

Пример

Ниже приведен пример статического класса, который содержит два метода, которые преобразуют температуру из Цельсия в Fahrenheit и из Fahrenheit в Цельсию:

public static class TemperatureConverter < public static double CelsiusToFahrenheit(string temperatureCelsius) < // Convert argument to double for calculations. double celsius = Double.Parse(temperatureCelsius); // Convert Celsius to Fahrenheit. double fahrenheit = (celsius * 9 / 5) + 32; return fahrenheit; >public static double FahrenheitToCelsius(string temperatureFahrenheit) < // Convert argument to double for calculations. double fahrenheit = Double.Parse(temperatureFahrenheit); // Convert Fahrenheit to Celsius. double celsius = (fahrenheit - 32) * 5 / 9; return celsius; >> class TestTemperatureConverter < static void Main() < Console.WriteLine("Please select the convertor direction"); Console.WriteLine("1. From Celsius to Fahrenheit."); Console.WriteLine("2. From Fahrenheit to Celsius."); Console.Write(":"); string? selection = Console.ReadLine(); double F, C = 0; switch (selection) < case "1": Console.Write("Please enter the Celsius temperature: "); F = TemperatureConverter.CelsiusToFahrenheit(Console.ReadLine() ?? "0"); Console.WriteLine("Temperature in Fahrenheit: ", F); break; case "2": Console.Write("Please enter the Fahrenheit temperature: "); C = TemperatureConverter.FahrenheitToCelsius(Console.ReadLine() ?? "0"); Console.WriteLine("Temperature in Celsius: ", C); break; default: Console.WriteLine("Please select a convertor."); break; > // Keep the console window open in debug mode. Console.WriteLine("Press any key to exit."); Console.ReadKey(); > > /* Example Output: Please select the convertor direction 1. From Celsius to Fahrenheit. 2. From Fahrenheit to Celsius. :2 Please enter the Fahrenheit temperature: 20 Temperature in Celsius: -6.67 Press any key to exit. */ 

Статические члены

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

Обычно объявлять нестатический класс с некоторыми статическими элементами, чем объявлять весь класс как статический. Два распространенных использования статических полей — сохранить количество объектов, созданных экземплярами, или сохранить значение, которое должно быть общим для всех экземпляров.

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

Хотя поле не может быть объявлено как static const , константное поле по сути статично в его поведении. Он относится к типу, а не к экземплярам типа. Таким образом, к полям можно получить доступ с помощью той же ClassName.MemberName нотации, const используемой для статических полей. Экземпляр объекта не требуется.

C# не поддерживает статические локальные переменные (то есть переменные, объявленные в методе область).

Для объявления статических методов класса используется ключевое слово static перед возвращаемым типом члена, как показано в следующем примере:

public class Automobile < public static int NumberOfWheels = 4; public static int SizeOfGasTank < get < return 15; >> public static void Drive() < >public static event EventType? RunOutOfGas; // Other non-static fields and properties. > 

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

Automobile.Drive(); int i = Automobile.NumberOfWheels; 

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

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

Спецификация языка C#

Дополнительные сведения см. в разделе «Статические классы», «Статические» и «элементы экземпляра» и «Статические конструкторы» в спецификации языка C#. Спецификация языка является предписывающим источником информации о синтаксисе и использовании языка C#.

См. также

  • static
  • Классы
  • class
  • Статические конструкторы
  • Конструкторы экземпляров

Совместная работа с нами на GitHub

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

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

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

Статические поля

Статические поля хранят состояние всего класса / структуры. Статическое поле определяется как и обычное, только перед типом поля указывается ключевое слово static . Например, рассмотрим класс Person, который представляет человека:

Person bob = new(68); bob.СheckAge(); // Уже на пенсии Person tom = new(37); tom.СheckAge(); // Сколько лет осталось до пенсии: 28 // получение статического поля Console.WriteLine(Person.retirementAge); // 65 // изменение статического поля Person.retirementAge = 67; class Person < int age; public static int retirementAge = 65; public Person(int age) < this.age = age; >public void СheckAge() < if (age >= retirementAge) Console.WriteLine("Уже на пенсии"); else Console.WriteLine($"Сколько лет осталось до пенсии: "); > >

В данном случае класс Person имеет два поля: age (хранит возраст человека) и retirementAge (хранит пенсионный возраст). Однако поле retirementAge является статическим. Оно относится не к конкретному человеку, а ко всем людям. (В данном случае для упрощения пренебрежем тем фактом, что в зависимости от пола и профессии пенсионный возраст может отличаться.) Таким образом, поле retirementAge относится не к отдельную объекту и хранит значение НЕ отдельного объекта класса Person, а относится ко всему классу Person и хранит общее значение для всего класса.

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

if (age >= retirementAge)

Но если мы хотим обратиться к этому полю вне своего класса, то мы можем обращаться к этому полю по имени класса:

Console.WriteLine(Person.retirementAge); Person.retirementAge = 67;

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

Статические методы класса в C#

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

Статические свойства

Подобным образом мы можем создавать и использовать статические свойства:

Person bob = new(68); bob.СheckAge(); Console.WriteLine(Person.RetirementAge); // 65 class Person < int age; static int retirementAge = 65; public static int RetirementAge < get < return retirementAge; >set < if (value >1 && value < 100) retirementAge = value; >> public Person(int age) < this.age = age; >public void СheckAge() < if (age >= retirementAge) Console.WriteLine("Уже на пенсии"); else Console.WriteLine($"Сколько лет осталось до пенсии: ") ; > >

В данном случае доступ к статической переменной retirementAge опосредуется с помощью статического свойства RetirementAge .

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

Нередко статические поля и свойства применяются для хранения счетчиков. Например, мы хотим иметь счетчик, который позволял бы узнать, сколько объектов Person создано:

var tom = new Person(); var bob = new Person(); var sam = new Person(); Console.WriteLine(Person.Counter); // 3 class Person < static int counter = 0; public static int Counter =>counter; public Person() < counter++; >>

В данном случае в классе Person счетчик хранится в приватной переменной counter, значение которой увеличивается на единицу при создании объекта в конструкторе. А с помощью статического свойства Counter, которое доступно только для чтения, мы можем получить значение счетчика.

Статические методы

Статические методы определяют общее для всех объектов поведение, которое не зависит от конкретного объекта. Для обращения к статическим методам также применяется имя класса / структуры:

Person bob = new(68); Person.CheckRetirementStatus(bob); class Person < public int Age < get; set; >static int retirementAge = 65; public Person(int age) => Age = age; public static void CheckRetirementStatus(Person person) < if (person.Age >= retirementAge) Console.WriteLine("Уже на пенсии"); else Console.WriteLine($"Сколько лет осталось до пенсии: ") ; > >

В данном случае в классе Person определен статический метод CheckRetirementStatus() , который в качестве параметра принимает объект Person и проверяет его пенсионный статус.

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

Статический конструктор

Кроме обычных конструкторов у класса также могут быть статические конструкторы. Статические конструкторы имеют следующие отличительные черты:

  • Статические конструкторы не должны иметь модификатор доступа и не принимают параметров
  • Как и в статических методах, в статических конструкторах нельзя использовать ключевое слово this для ссылки на текущий объект класса и можно обращаться только к статическим членам класса
  • Статические конструкторы нельзя вызвать в программе вручную. Они выполняются автоматически при самом первом создании объекта данного класса или при первом обращении к его статическим членам (если таковые имеются)

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

Определим статический конструктор:

Console.WriteLine(Person.RetirementAge); class Person < static int retirementAge; public static int RetirementAge =>retirementAge; static Person() < if (DateTime.Now.Year == 2022) retirementAge = 65; else retirementAge = 67; >>

В данном случае с помощью встроенной структуры DateTime получаем текущий год. Для этого используется свойство DateTime.Now.Year . если он равен 2022, устанавливаем один пенсионный возраст. При другом значении текущего года устанавливается другое значение пенсионного возраста.

Статические классы

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

Console.WriteLine(Operations.Add(5, 4)); // 9 Console.WriteLine(Operations.Subtract(5, 4)); // 1 Console.WriteLine(Operations.Multiply(5, 4)); // 20 static class Operations < public static int Add(int x, int y) =>x + y; public static int Subtract(int x, int y) => x - y; public static int Multiply(int x, int y) => x * y; >

Как создать статический класс java

Статический класс в Java — это класс, который определен как статический внутри другого класса. Объекты статического класса могут быть созданы без создания объектов внешнего класса.

Для создания статического класса в Java , нужно использовать ключевое слово static при определении класса внутри другого класса. Например:

public class OuterClass  // код внешнего класса static class StaticNestedClass  // код статического вложенного класса > > 

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

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 ).

  1. 35. Отметим попутно, что шаблон «одиночка» в современной практике часто стремятся заменять на шаблон «инверсии управления».↩︎

Добавить комментарий

Ваш адрес email не будет опубликован. Обязательные поля помечены *