Вопрос по c#, static – Почему C # не поддерживает локальные статические переменные, как C?

58

Почему в C # нет локальных статических переменных, таких как C? Мне этого не хватает!!

Я знаю, что означает C # static ... мой вопрос использует static в смысле C JoelFan
Это больше, чем пустяки - кажется, что разработчики языка пришли к противоположным выводам. Было бы интересно узнать почему. Jon
Вы должны спросить дизайнеров. John Saunders
@Atomosk Любое место, где вы хотите, чтобы значение было одинаковым при каждой загрузке функции, без необходимости показывать его полному классу. AustinWBryan
Общая информация: VB.NET поддерживает это с помощьюStatic ключевое слово. vcsjones

Ваш Ответ

12   ответов
29

object или частьtypeне является частью метода. (Исключение составляют захваченные переменные, конечно.)

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

Просто говоря, я нахожусь в ситуации, когда мне нужна статическая переменная, которая будет использоваться только в одном методе, и создание этой переменной в определении класса приведет к (нежелательному) коду спагетти.
Обратите внимание, что в C ++ вы можете использовать статические & quot; переменные & quot; для постоянных значений. Поскольку C # имеет только константы в математическом смысле, вы получаете очень громоздкий способ работы с локальными, вычисляемыми константами (например, sqrt of 2, просто пример).
@JimBalter: я все еще не согласен. Состояние по своей природе связано с типом (оно живет, пока существует домен приложения, и не существует, например, до инициализации типа). Я был бы признателен, если бы несогласие могло быть выражено без нападок ad hominem, хотя ...
Обе эти опции - обходные пути, так как они имеют большую видимость, чем просто локальная функция. Создание нового класса - это «уборщик» решение, но теперь вам нужно больше кода, чтобы обойти отсутствующую функцию.
@Ken: Но я хочу сказать, что это сам по себе запах дизайна. Если бы компилятор поддерживал это, он должен был бы поднять переменную, чтобы она была экземпляром или статической переменной, поэтомуwould быть частью состояния вашего класса или экземпляра в любом случае. Если это неприемлемо для вашего класса, тогда оно неприемлемо, однако оно представлено в исходном коде. Если оноis уместно, почему вы хотите скрыть это состояние?
27

Почему C # не поддерживает статические переменные метода? имеет дело с точным вопросом, заданным в оригинальном сообщении:

There are two reasons C# doesn't have this feature.

First, it is possible to get nearly the same effect by having a class-level static, and adding method statics would require increased complexity.

Second, method level statics are somewhat notorious for causing problems when code is called repeatedly or from multiple threads, and since the definitions are in the methods, it's harder to find the definitions.

[Author: Eric Gunnerson]

Я понимаю, что это не обязательно ВАШЕ мнение, но можно привести точно такой же аргумент для статики на уровне класса. «Многопоточность сложна и ошибочна» и «множественные пользователи, изменяющие одно и то же статическое значение в памяти, более подвержены ошибкам и их трудно отследить»; нет оправдания, чтобы не включать функцию. Это проблемы с существующим набором функций, которые не особенно усугубляются этой функцией. Ради бога, у них есть статические классы и статические конструкторы, которые даже БОЛЬШЕ известны тем, что их трудно отследить и даже сложнее проводить юнит-тесты!
0

и метод является общим для всех экземпляров.

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

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

Если вы ими не пользуетесь, то и рефакторинг станет легче позже.

Я думаю, что слово «статический» Сам должен прояснить, что есть только 1 переменная JoelFan
В очень немногих случаях вам нужно хранить переменную "только для одного" внутри метода, вы должны спросить себя, почему вы рассеяли информацию о состоянии общего класса с помощью нескольких методов.
Ли: Обычно, потому что вы имеете дело с повторным входом или какой-то другой похожей проблемой, связанной с методом. В этих случаях логически более логично связывать состояние с методом, чем со всем классом, даже если нет различий в способе, которым оно реализовано в CLR, по сравнению с обычным частным статическая переменная
0

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

Если вы думаете, что это будет большое логическое изменение, мне было бы интересно узнать, как это сделать.

class MyClass
{
    public static float MaxDepthInches = 3;

    private void PickNose()
    {
        if (CurrentFingerDepth < MyClass.MaxDepthInches)
        {
            CurrentFingerDepth++;
        }
    }
}
Вот два больших логических изменения: 1) оно является общедоступным и поэтому может быть изменено кодом вне класса; Это означает, что другие люди, использующие ваш класс, могут изменить его способами, непредсказуемыми для вашего кода. Излишне говорить, что это обычно плохо. 2) Область действия переменной - весь класс, а не только один метод. Изменение области действия с одного метода на весь класс - довольно большое изменение, даже если вы объявляете его закрытым.
0

того. Поскольку C # ограничивает область действия статических переменных классами, вы можете использовать nested-class в качестве области действия.

Например:

public class Foo {
    public int Increment() {
        return IncrementInternal.Increment();
    }

    private static class IncrementInternal {
        private static int counter = 0;
        public static int Increment() {
            return counter++;
        }
    }
}

ВотFoo опорыIncrement метод, но его поддержка частным вложенным классомIncrementInternal который содержит статическую переменную в качестве члена. И, конечно же,counter не виден в контексте (другие методы)Foo.

Кстати, если вы хотите получить доступ кFoo контекст (другие члены и методы) внутриIncrementInternal.Increment, вы можете пройтиthis в качестве параметра дляIncrementInternal.Increment когда вы звоните изFoo.

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

Я думаю, что это чище, чем анонимные функции или IIFE.

Вы можете увидеть живую демонстрациюВот.

-3

public Func<int> Increment()
{
    int num = 0;
    return new Func<int>(() =>
    {
        return num++;
    });
}

Вы можете назвать это так:

 Func<int> inc = Increment();
 inc();
Это ничего не решает. Каждый раз, когда вы вызываете метод, вы получаете новыйIncrement функция, сnum = 0, Если вы изменитеIncrement метод статический, почему бы просто не использоватьprivate static num = 0; ? Вы ничего не получите от этого.
Нет, этот ответ действительно имеет смысл, за исключением того, что он должен быть статичным. Почему бы вам не добавить приватную статику, которую вы говорите, но это действительно вопрос, почему нет локальной статики. Очевидно, что ответом является то, что вы предлагаете, почему бы не использовать вместо этого классическую локальную статику? Теперь мы имеем дело с реальным вопросом: классовая статика против локальной статической, что лучше, ну и контекст, требуемый для локальной статической, не очень прост.
Принятый ответ имеет оценку -3. WTH?
Другой ответ был опубликован полтора года назад с таким же общим подходом.
Очень JavaScriptish JoelFan
1

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

Тем не менее, вы можете достичь почти такого же эффекта в C #, создав вложенный класс.

Вложенный класс имеет доступ к закрытым переменным класса вложения. Затем вы можете сделать переменную приватной и поместить туда свой единственный метод, но вам все равно придется отправить ему экземпляр класса вложенности. Конечно, вы можете сделать это расширением класса. Тогда никто не станет мудрее.
111

чтобы удовлетворить себя.

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

Несомненно, C # является чистым и whatchamacallit-ориентированным. Вот почему они автоматически генерируют постоянные локальные значения для лямбда-функций. Все так сложно. Я чувствую себя так глупо

Статическая петля полезна и важна во многих случаях.

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

+1 согласен, большинство ответов здесь заканчиваются на "вы можете достичь этого с помощью ... бла", но этого не хватает. Я мог достичь желаемого поведения разными способами, но это не главное. Статические локальные переменные точно такие же, как статические переменные класса, но с меньшей видимостью, что является желаемым поведением.
На самом деле, VB.NET просто создает статические переменные уровня класса за кулисами. Переменные статического метода не поддерживаются CLR.
Каждый раз, когда вы говорите "все, что вы можете сделать ... вы также можете делать" должен закончиться тем, почему вы не программируете машину Тьюринга? Возможность сузить область действия переменной отдельно от ее времени жизни очень полезна, и поля статических экземпляров не препятствуют другим разработчикам вмешиваться, когда они не должны - иначе зачем беспокоиться о public / private / etc.
На самом деле это возможно в .NET, но не в C #. Например, VB.NET поддерживает локальные статические переменные.
Я хотел бы дать этот ответ несколько тысяч голосов. Для меня дело не столько в защите от подделки, сколько в разъяснении целей. Для будущего меня. Как завтра или позже сегодня. Не всегда так легко (часто невозможно) назвать что-либо, чтобы все в нем сразу стало очевидным. Ограничение объема чрезвычайно помогает в этом. Колоссально. Нет достаточных оснований для того, чтобы не ограничивать объем статики. Да, иногда вы можете понять, что вам нужен доступ к нему вне функции. Так что, если / когда это произойдет, проведите рефакторинг и дайте ему большее имя, больше документов и большую область видимости.
7

вы хотите использовать статическую локальную переменную в своем методе? Поздравляем! Вы сделали еще один шаг к тому, чтобы статьreal программист.

Не слушайте всех людей, говорящих вам, что статические локальные объекты не являются «чистыми», что они препятствуют «читабельности» и может привести к неуловимым и труднодоступным «ошибкам». Ерунда! Они просто говорят это, потому что ониwannabe программисты! Многие из них, вероятно, даже играют с эзотерическим функциональным языком программирования в свободное время. Ты можешь в это поверить? Какая куча хипстеров!

Real программисты принимают парадигму, которую я люблю называтьSDD - Side effect Driven Design, Вот некоторые из наиболее важных законов:

Don't be predictable! Never return the same thing from a method twice - even if it's being called with the exact same arguments!

Screw purity - let's get dirty! State, by nature, craves changing, because it is an insatiable monoid in the category of polyamorous endofunctors, i.e. it likes to be touched by as many collaborators as possible. Never miss out on an opportunity to do it the favor!

Среди инструментов, используемых для кодирования побочным эффектом, есть, конечно же, статические локальные переменные. Однако, как вы заметили, C # не поддерживает их. Зачем? Потому что за последние два десятилетияMicrosoft has been infiltrated by so called Clean Coders что поддерживает ремонтопригодность, а не гибкость и контроль. Можете ли вы вспомнить, когда в последний раз видели наш любимый синий экран? Теперь угадайте, чья это вина!

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

Однако предоставленное решение не было вполне удовлетворительным. С использованиемпредыдущий ответ наш почти SDD-совместимый код будет выглядеть примерно так:

var inc = Increment();
var zero = inc();
var one = inc();

или же

var zero = Increment()();

Но это просто глупо. Дажеwannabe разработчик может видеть, чтоIncrement() это не нормальный метод и станет подозрительным.real программист, с другой стороны, может сделать его еще более SDD-подобным. Он или она знает, что мы можем сделать свойство или поле похожим на метод, задав ему типFunc<T>! Нам просто нужно инициализировать его, выполнив лямбду, которая, в свою очередь, инициализирует счетчик и возвращает другую лямбда, увеличивающую захваченный счетчик!

Вот это в правильном SDD-коде:

public Func<int> Increment = new Func<Func<int>>(() =>
{
    var num = 0;
    return () => num++;
}).Invoke();

(Вы думаете, что вышеупомянутый вид выглядит какIIFE? Да, вы правы и вам должно быть стыдно за себя.)

Теперь каждый раз, когда вы звонитеIncrement() это вернет что-то другое:

var zero = Increment();
var one = Increment();

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

Это покажет имwannabe программисты!

@ Клайс, конечно, это серьезно! Это единственный ответ, показывающий, как должным образом эмулировать статические локальные переменные, поэтому со стороны почти не видно различий между обычным методом C # и методом C # ». который управляет статическими местными жителями.
Это хильarious! Я никогда не думал о том, чтобы замаскировать такое свойство, как этот метод ... Возможно, потому что это один из немногих случаев, когда вам даже хотелось
Это должно быть серьезным ответом на этот вопрос? Это больше похоже на древний вопрос, который был взят и использован в качестве платформы для разглагольствования о "других людях". плохие привычки программирования.
@GoodNightNerdPride Я пробовал ваше решение, и оно мне действительно нравится. К сожалению, он не работает для ссылочных типов (например,List<>и т.д., которые я инициализируюnull). Есть ли способ заставить его работать?Func всегда возвращаетсяnull, даже если я изменю возвращенный список в методах, которые вызываютFunc, Я думал, что ссылка будет, так сказать, «прилипать».
@silkfire Итак, вы хотите вмешаться в «статическую локальную переменную» снаружи? Это дух SSD! Но не будем шутить: ссылочные типы C # - это ссылки на объекты, а не ссылки на ссылки! То, что вы в основном пытаетесь сделать, это:dotnetfiddle.net/wd73MS Обратите внимание, что на втором отпечатке все еще отображается старое число, поскольку ссылка на список не может быть перенаправлена на новый список извне. Однако вы можете изменить список, вызвавAdd(), Remove() и т.д. на это.
1

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

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

-3

ворт, объявляющуюХОРОШИЕ НОВОСТИ ВСЕМ!C # 6.0 позволяетusing static заявление. Это позволяет эффективно импортировать методы статического класса (а также свойства и члены) в глобальную область видимости.

Короче говоря, вы можете сделать что-то вроде этого:

using NUnit.Framework;
using static Fizz.Buzz;

class Program
{
    [Test]
    public void Main()
    {
        Method();
        int z = Z;
        object y = Y;
        Y = new object();
    }
}


namespace Fizz
{
    class Buzz
    {
        public static void Method()
        {
        }

        public static int Z;

        public static object Y { get; set; }
    }
}   

Хотя это доступно только в C # 6.0, насколько я понимаю, сгенерированные сборки должны быть совместимы с предыдущими платформами .NET (поправьте меня, если я ошибаюсь).

Это не имеет отношения к вопросу. JoelFan
15

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

Кроме того, Эрик Липперт отвечает на такие вопросы на своемблог много. Вообще ответил всюда: "Меня спрашивают", почему "C # не реализует функцию X?" все время. Ответ всегда один и тот же: потому что никто никогда не проектировал, не определял, не реализовывал, не тестировал, не документировал и не поставлял эту функцию. & Quot; По сути, его ответы, как правило, сводятся к тому, что добавить какую-либо функцию стоит денег, и, следовательно, многие потенциальные функции не реализованы, поскольку они не проявили положительной стороны анализа затрат и выгод.

Я не мог бы сказать это лучше сам.
I believe you can accomplish everything you could with a local static, by using a class level static that is only used for one method. - соломенный Все это знают, но это совершенно не имеет значения, потому что это не касаетсяwhy люди хотят методики локальных статик - лексическая локальность является важным принципом, и многие линтеры сообщают об этом. Второй абзац похожvirtus dormitiva или жеle meilleur des mondes possibles - универсальное рукопожатие, которое не решает вопрос. (К счастью, C # добавил много хороших возможностей с тех пор, как EL покинул команду.)
Проблема с реализацией локальной статики с помощью статического класса заключается в том, что пространство имен класса излишне загрязняется. Проблема требований к ресурсам для добавления функций в C # заключается в том, что большинство языков от Microsoft (и других, кашель, Sun / Oracle) не являются фантастическими. Они непозволительно делают то, что "достаточно хорошо" не то, что хорошо.
Я могу уважать вторую часть к этому ответу. Первая часть - это решение, но не оправдание. Это важная часть, чтобы увидеть здесь. Как уже говорили другие, говоря: «Вы можете достичь Х, выполнив Y». ничем не отличается от & quot; вы можете смоделировать все функции любого языка программирования с помощью машины Тьюринга. & quot; И тем не менее мы этого не делаем, потому что, несмотря на то, что это «достаточно мощный инструмент», язык, который выполняет те же функции, что и машина Тьюринга (кроме бесконечной памяти, конечно), заключается в том, что он недостаточно продуктивен.

Похожие вопросы