Преимущества использования частных статических методов

209

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

Пример:

foreach (XmlElement element in xmlDoc.DocumentElement.SelectNodes("sample"))
{
    string first = GetInnerXml(element, ".//first");
    string second = GetInnerXml(element, ".//second");
    string third = GetInnerXml(element, ".//third");
}

...

private static string GetInnerXml(XmlElement element, string nodeName)
{
    return GetInnerXml(element, nodeName, null);
}

private static string GetInnerXml(XmlElement element, string nodeName, string defaultValue)
{
    XmlNode node = element.SelectSingleNode(nodeName);
    return node == null ? defaultValue : node.InnerXml;
}

Есть ли какое-либо преимущество в объявлении методов GetInnerXml () как статических? Нет мнений, пожалуйста, у меня есть мнение.

NerdFury
источник

Ответы:

221

Со страницы правил FxCop об этом:

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

Скотт Дорман
источник
37
Я также добавил бы, что «статическое» предложение не наносит вреда и уже предоставляет некоторую «документацию» с одним словом. Он говорит вам, что этот метод не использует какого-либо члена экземпляра, и вы получаете эту документацию почти бесплатно
frandevel
20
Я бы даже сказал: «Если метод не нуждается в доступе к состоянию (это), сделайте его статическим», как правило.
DanMan
3
В интересах баланса стоит отметить, что многие люди, как правило, против статических методов, потому что они нарушают полиморфизм и означают, что объект не может быть заглушен для тестирования. например, см. googletesting.blogspot.co.uk/2008/12/…
Энди
@ Энди - Хороший вопрос. Один из способов нарисовать линию состоит в том, чтобы посмотреть, имеет ли статический метод доступ к чему-либо вне параметров, которые вы передаете. Пока он самодостаточен, его легко проверять, и в этом нет необходимости. чтобы заглушить что-нибудь.
Нил
4
Многие разработчики не знакомы с «приватной статикой». Я использовал его в общей кодовой базе моей команды, и это привело к путанице. В свою очередь, это дает очень незначительную выгоду. Мы могли бы обучить всех в команде, включая всех будущих разработчиков, поддерживающих код, тому, что он означает. Но выгода от переключения частного метода на частный static настолько незначительна (то есть устраняется зависимость от данных экземпляра), что не стоит усилий и путаницы. В любом случае метод уже закрытого типа. Это особенность языка, которую на самом деле не нужно знать.
Кертис
93

Когда я пишу класс, большинство методов делятся на две категории:

  • Методы, которые используют / изменяют текущее состояние экземпляра.
  • Вспомогательные методы, которые не используют / не изменяют текущее состояние объекта, но помогают мне вычислять значения, которые мне нужны в другом месте.

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

Возьмите этот пример:

библиотека публичного класса
{
    private static Book findBook (Список <Book> книг, строковое название)
    {
        // код идет сюда
    }
}

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

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

Нил
источник
1
Вид объявления const-метода в C ++, не так ли?
Anhoppe
Да - это еще один хороший способ использования языка для упрощения вещей путем ограничения того, что может пойти не так.
Нил
Это не обязательно правда. Давайте предположим, что у a Libraryесть поле экземпляра List<Book> _booksдля хранения его книг (не так, как вы, Libraryвероятно, разработали бы класс, но с помощью w / e), и он передает этот список findBook, и что статический метод вызываетbooks.Clear() или books.Reverse()и так далее. Если вы предоставляете статическому методу доступ к ссылке на какое-либо изменяемое состояние, то этот статический метод может очень испортить ваше состояние.
Сара
1
Правда. И в этом случае подпись покажет нам, что этот метод имеет доступ (и возможность мутировать) к экземпляру библиотеки.
Нил
Практически для любой защитной конструкции, которую мы можем использовать, есть какой-то способ ее подорвать. Но их использование все же разумно и помогает подтолкнуть нас в правильном направлении, к «яме успеха».
Нил
81

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

источник: MSDN - http://msdn.microsoft.com/en-us/library/79b3xss3(v=vs.110).aspx

Марек Такач
источник
15

Да, компилятору не нужно передавать неявный thisуказатель на staticметоды. Даже если вы не используете его в своем методе экземпляра, он все еще передается.

Кент Бугаарт
источник
Как это связано с преимуществами производительности или памяти во время выполнения?
Скотт Дорман
11
Передача дополнительного параметра означает, что ЦП должен выполнить дополнительную работу, чтобы поместить этот параметр в регистр и поместить его в стек, если метод экземпляра вызывает другой метод.
Кент Boogaart
5

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

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

Свободный антилоп гну
источник
4

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

Джоэл Коухорн
источник
То, что это переменная класса, не означает, что она должна быть статической.
Скотт Дорман
3
Нет, но если он используется статическим методом, то он ДОЛЖЕН быть статическим. Если бы метод не был статическим, то вы, возможно, не сделали бы член класса статическим, и это привело бы к увеличению памяти, используемой для каждого экземпляра класса.
Джоэл Коухорн
2

Я очень предпочитаю, чтобы все частные методы были статическими, если они действительно не могут быть. Я бы предпочел следующее:

public class MyClass
{
    private readonly MyDependency _dependency;

    public MyClass(MyDependency dependency)
    {
        _dependency = dependency;
    }

    public int CalculateHardStuff()
    {
        var intermediate = StepOne(_dependency);
        return StepTwo(intermediate);
    }

    private static int StepOne(MyDependency dependency)
    {
        return dependency.GetFirst3Primes().Sum();
    }

    private static int StepTwo(int intermediate)
    {
        return (intermediate + 5)/4;
    }
}

public class MyDependency
{
    public IEnumerable<int> GetFirst3Primes()
    {
        yield return 2;
        yield return 3;
        yield return 5;
    }
}

по каждому методу, получающему доступ к полю экземпляра. Почему это? Поскольку, поскольку этот процесс вычисления становится более сложным, и класс заканчивается 15-ю закрытыми вспомогательными методами, я ДЕЙСТВИТЕЛЬНО хочу иметь возможность вывести их в новый класс, который инкапсулирует подмножество шагов семантически значимым образом.

Когда MyClassпоявляется больше зависимостей, потому что нам нужно вести логи, а также нужно уведомить веб-службу (пожалуйста, извините за примеры клише), тогда очень полезно легко увидеть, какие методы имеют какие зависимости.

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

Сара
источник
-3

Как уже говорилось, у статических методов есть много преимуществ. Тем не мение; имейте в виду, что они будут жить в куче на протяжении всей жизни приложения. Недавно я провел день, отслеживая утечку памяти в службе Windows ... утечка была вызвана частными статическими методами внутри класса, который реализовал IDisposable и был последовательно вызван из оператора using. Каждый раз, когда этот класс создавался, в куче зарезервировалась память для статических методов внутри класса, к сожалению, когда класс был удален, память для статических методов не освобождалась. Это привело к тому, что занимаемая памятью служба потребляет доступную память сервера в течение нескольких дней с предсказуемыми результатами.

Джеймс Хауманн
источник
4
Это не имеет никакого смысла. Куча никогда не хранит память для кода для любых методов, статических или иных. Куча для экземпляров объекта. В стеке будут данные для любого вызова любого метода (для хранения памяти для параметров, возвращаемого значения, неподъемных локальных объектов и т. Д.), Но все это исчезнет, ​​когда метод завершит выполнение.
Servy