Если это относится к конкретному экземпляру, то он должен быть элементом экземпляра (будь то метод, свойство или поле). Это наиболее распространенные случаи, поэтому примеров множество.
Если это не относится к конкретному экземпляру, тогда члену экземпляра требуется экземпляр, который вы не будете использовать никаким другим способом. Хорошим примером является Math.Max
, если вы вызываете Math.Max(43, 23)
, то результат относится к тому факту, что 43 больше 23, а не к какому-либо свойству объекта Math
, которое может измениться в ходе работы приложения.
Некоторые классы нуждаются только в статических членах, поэтому мы делаем сам класс статическим, и его создание вообще невозможно.
Свойства, которые относятся к природе класса, а не к конкретному экземпляру, также должны быть статическими по той же причине. Например. int.MaxValue
является свойством int
, а не, например, 93.
Обратите внимание, что результат int.MaxValue
сам по себе int
. Это не редкость. Другие примеры включают TimeSpan.Zero
и string.Empty
. Это может быть удобством, а иногда и преимуществом производительности в предотвращении большого количества дублирующих ссылочных типов (не имеет значения в случае типов значений и не должно быть завышено в случае ссылочных типов). Важно не переусердствовать. Мы бы не хотели, чтобы 4294967296 различных статических свойств на int
делали их «простыми» для их вызова! Обычно это полезно, когда:
Особый случай не может быть сконструирован конструктором.
OR
Особый случай обычно используется (TimeSpan.Zero
) и / или неудобен для запоминания (int.MaxValue
более понятен и легче запомнить, чем 2147483647
или даже 0x7FFFFFFF
). Тем более, если, конечно, оба.
Методы расширения - это статические методы, которые можно вызывать так, как если бы они были членами экземпляра. Они очень удобны, но обычно лучше использовать элемент экземпляра, когда это возможно. Они полезны, когда член экземпляра невозможен, потому что:
- У вас нет доступа к источнику класса (это класс другой стороны).
- Вы хотите определить его на интерфейсе, а не на классе.
- Вы хотите, чтобы он вызывался на нуле (избегайте, это не идиоматично в C #, хотя чаще встречается в других языках).
- Вы хотите определить его для особых случаев обобщений. Например, если я создал
MyDictionary<TKey, TValue>
, который реализовал IDictionary<TKey, TValue>
, я не смогу определить метод plus
, который добавляет число к сохраненному значению, потому что он может работать только тогда, когда TValue является известным числовым типом. Я могу определить такой метод как метод расширения, например int Plus<TKey>(this MyDictionary<TKey, int> dict, int addend)
, который будет отображаться как член экземпляра, когда TValue
имеет тип int, но не будет мешать использованию MyDictionary
для других параметров типа.
Во всех этих случаях у вас нет выбора, кроме как использовать метод расширения, но не используйте его, когда член экземпляра выполнит эту работу. Это понятнее, особенно если учесть, что некоторые другие языки .NET будут видеть элемент расширения только как статический.