Лучшая практика для статических методов и методов экземпляров, когда требуются значения - PullRequest
0 голосов
/ 12 января 2012

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

Когда у вас есть класс с некоторыми свойствами и метод в этом классе, который должен использовать эти свойства, лучше ли делать это с помощью метода static или экземпляра?

IE

class Foo
{
    //properties
    bar1;
    bar2;

    //method
    sumbar1andbar2()
    {
        return bar1 + bar2;
    }
}

Метод sumbar1andbar2 требует наличия обоих свойств класса Foo.Кажется немного глупым создавать статический метод и вызывать его таким образом, поскольку я вручную передаю члены класса в метод класса:

Foo foo1 = new Foo();
foo1.bar1 = x;
foo1.bar2 = y;
sumbar1andbar2(foo1.bar1, foo1.bar2); 

Но хотя приведенный ниже метод экземпляра выглядит намного чищеЯ не знаю простого и понятного способа гарантировать, что и bar1, и bar2 не равны NULL, что может вызвать исключение:

Foo foo1 = new Foo();
foo1.bar1 = x;
foo1.bar2 = y;
sumbar1andbar2();

Однако метод экземпляра кажется еще лучше, если метод модифицируетсядругое свойство класса, например bar3.

Ответы [ 3 ]

2 голосов
/ 12 января 2012

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

Если это не относится к конкретному экземпляру, тогда члену экземпляра требуется экземпляр, который вы не будете использовать никаким другим способом. Хорошим примером является 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). Тем более, если, конечно, оба.

Методы расширения - это статические методы, которые можно вызывать так, как если бы они были членами экземпляра. Они очень удобны, но обычно лучше использовать элемент экземпляра, когда это возможно. Они полезны, когда член экземпляра невозможен, потому что:

  1. У вас нет доступа к источнику класса (это класс другой стороны).
  2. Вы хотите определить его на интерфейсе, а не на классе.
  3. Вы хотите, чтобы он вызывался на нуле (избегайте, это не идиоматично в C #, хотя чаще встречается в других языках).
  4. Вы хотите определить его для особых случаев обобщений. Например, если я создал MyDictionary<TKey, TValue>, который реализовал IDictionary<TKey, TValue>, я не смогу определить метод plus, который добавляет число к сохраненному значению, потому что он может работать только тогда, когда TValue является известным числовым типом. Я могу определить такой метод как метод расширения, например int Plus<TKey>(this MyDictionary<TKey, int> dict, int addend), который будет отображаться как член экземпляра, когда TValue имеет тип int, но не будет мешать использованию MyDictionary для других параметров типа.

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

2 голосов
/ 12 января 2012

Если поведение метода уникально для типа Foo (и не применимо в других местах) или если оно изменяет состояние Foo, то вам, вероятно, следует сделать его методом экземпляра Foo.

Если это общий расчет (как в вашем примере), где вы можете использовать его в другом месте, у вас есть несколько вариантов:

Сделайте его статическим методом в служебном классе, например,

public static class MyUtility {
    public static Int32 Add(Int32 x, Int32 y) { return x + y; }
}

Сделайте это методом расширения в Foo, его родительском классе или интерфейсе, который определяет x и y, например,

// Use as follows:
// var f = new Foo() { x = 5, y = 5 };
// var ten = f.MyUtility();
public static class MyUtility {
    public static Int32 Add(this Foo foo) { return Foo.x + Foo.y; }
}
1 голос
/ 12 января 2012

Во-первых, есть хороший и простой способ убедиться, что ваши свойства не равны NULL: это называется encapsulation .Убедитесь, что свойства установлены в конструкторе, и проведите проверку в их установщике, если вы решите выставить его.Таким образом, свойства будут ненулевыми после создания объекта (в противном случае конструктор вызовет исключение), а установщики свойств оставят значения свойств в согласованном состоянии (в противном случае сеттеры выбросят исключение).

Теперь перейдем к актуальному вопросу: если одно или оба значения могут потенциально исходить из других экземпляров Foo, сделайте метод расчета статическим.В противном случае сделайте его методом экземпляра.

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

Добро пожаловать на сайт PullRequest, где вы можете задавать вопросы и получать ответы от других членов сообщества.
...