Понимание статического ключевого слова

16

У меня есть некоторый опыт разработки с Java, Javascript и PHP.

Я читаю Microsoft Visual C # 2010 «Step by Step», и я считаю, что это очень хорошая книга о том, как познакомить вас с языком C #.

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

Но какова реальная цель статического ключевого слова? Когда я должен объявить статическую переменную и методы?

Нистор Александру
источник
4
static в C # почти такой же, как static в Java. Если вы понимаете это в Java, у вас не должно быть проблем в C #
superM
Я был моим первым языком программирования, и я тоже не понимал этого понятия. Я использовал Java только в течение короткого периода времени
Nistor Alexandru
Вкратце: используйте «статический», когда вам не нужна ориентация объекта, например, только некоторые автономные методы или переменные. Объявление класса как статического означает поместить эти не объектно-ориентированные функции и переменные просто в общее имя (пространство), имя класса.
Док Браун

Ответы:

15

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

public class SomeObject
{
    //Static Field
    static int Foo = 3;

    //instance field
    private int _Foo2 = 4;

    //instance property
    public int Foo2{get{return _Foo2;}set{_Foo2 = value;}}


    //static factory method
    public static SomeObject CreateSomeObject(int fooValue)
    {
        SomeObject retVal = new SomeObject();
        retVal.Foo2 = fooValue;
        return retVal;
    }

    //Parameterless instance constructor
    public SomeObject()
    {
    }

    public static int Add(int x)
    {
        //Static methods can only deal with local variables, or fields that
        //  are also static in the class.  This one adds x to the static member foo
        return x + Foo;

        //Foo2 is not accessable here!
    }

      //Instance method
    public int AddSomething(int x)
    {
        //Add x to the property value of Foo2
        return x + this.Foo2;

        //Note that Foo *is* accessable here as 'SomeObject.Foo'
    }

}

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

В любом случае, существует специальные шаблоны проектирования для использования статических методов, такие как фабричная модель и одноплодной шаблон , но важно помнить, что статические методы и конструкторы не имеет дела с каким - либо конкретным экземпляром класса (если вы не разъехаться в), как правило, для расчетов или сравнения объектов. Метод «Main», на который вы ссылаетесь, всегда статичен, но чтобы увидеть его с другой точки зрения, обратитесь к этой статье .

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

public static void Main(string[] args)
{
    //This is a static method that starts a thread in an application
    // space.  At this point not everything actually has to be static...

    //Here is an instantiation with a parameterless contruction
    SomeObject obj = new SomeObject();

    //Here is an instantiation using a static factory method
    SomeObject obj2 = SomeObject.CreateSomeObject(3);

    //Getting field value from static field
    // Notice that this references the class name, not an instance
    int fooValue1 = SomeObject.Foo;

    //Getting property value from instance
    //  Note that this references an object instance
    int fooValue2 = obj2.Foo2;

    //Instance method must be called through an object
    obj2.AddSomething(4);  //if default constructor, would return 8

    //Static methods must be called through class name
    SomeObject.Add(4); //Returns 7
}

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

iMortalitySX
источник
18

Вот способ объяснения Джошуа Блоха, который я нахожу блестящим, как большинство из того, что он говорит (да, я фанат Джошуа Блоха :)). Это цитата из памяти.

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

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

Тем не менее, у вас может быть некоторое поведение, которое применимо к черновому варианту напрямую, и которое вы можете использовать / использовать непосредственно в черновом наборе, не создавая фактического дома из этого чертежа. Представьте, что у вашего чертежа есть кнопка, которая после нажатия будет отображать площадь дома, содержащуюся в этом чертеже (вычисляя все длины стен и тому подобное). Очевидно, что вы МОЖЕТЕ сначала построить дом, а затем приступить к измерению его площади, но вы можете сделать это только с одним черновиком, так что было бы более полезно, чтобы это поведение было реализовано в черновике. Такая встроенная кнопка с черным шрифтом, которая вычисляет площадь дома, является эквивалентом наличия статической функции в классе.

Дракон Шиван
источник
Или у вас будет Blueprintкласс, который реализует функциональность проекта, в том числе возможность рассчитать площадь дома, выраженную в проекте. Этот экземпляр проекта затем передается Builder(в свою очередь, скорее всего, экземпляру), который, в свою очередь, делает все необходимое для создания и вывода потенциально произвольного числа Buildingэкземпляров на основе проекта.
CVn
12

Глядя на это таким образом, мне помогает:

  • Каждый тип имеет статический экземпляр.
  • Статический экземпляр создается при первом обращении к типу - либо через статический экземпляр, либо при создании другого экземпляра.
  • Вы можете создать столько нестатических экземпляров, сколько захотите, но есть только один статический экземпляр.
  • Все внутри класса, которое объявлено как статическое, принадлежит статическому экземпляру и, следовательно, не имеет доступа ни к каким другим экземплярам, ​​которые вы создаете. Но другие экземпляры имеют доступ к статическому экземпляру.
  • Если класс объявлен как статический, вы не можете создавать другие экземпляры, только статический экземпляр может существовать.
  • Вы можете объявить статический конструктор для статического экземпляра так же, как конструктор для обычного экземпляра (но объявив его статическим).

Что касается того, когда использовать ключевое слово static:

  • Любой метод, которому не нужен доступ к локальным свойствам, может и, вероятно, должен быть объявлен как статический.
  • Вспомогательные классы, которые вообще не имеют какого-либо состояния (что в любом случае должно быть редким) и которые никогда не будут имитироваться, могут быть объявлены статическими. Должны ли они это другое дело; используйте эту функциональность экономно.
  • Свойства и поля, к которым должны обращаться все экземпляры класса, должны быть объявлены как статические. Но используйте это только тогда, когда нет другого выбора.
прецизионный самописец
источник
+1, для хорошего резюме, я не знал, что у каждого типа есть 1 статический экземпляр, и я нахожу странным говорить вам правду.
NoChance
2
@EmmadKareem Это просто ментальная модель, которую использует pdr, потому что "Looking at it this way helps"он. Вы находите это странным, потому что это не совсем верно, но вы можете думать об этом так, если хотите. Вы знаете модель Бора? Это набор правил и идей о том, как атомы и электроны взаимодействуют друг с другом. Модель работает в зависимости от того, что вы делаете, но это не реальность.
phant0m
@ phant0m, спасибо за объяснение, у меня сложилось впечатление, что это была настоящая, а не модель, и я был удивлен этим.
NoChance
На самом деле, иногда есть причины, по которым вы не захотите создавать метод, staticдаже если он не использует локальные свойства. Создание вещей staticможет добавить связь с клиентами, потому что они должны обращаться к классу напрямую. Например, это может усложнить юнит-тестирование с макетом.
Аллан
@Allan: Возможно, если вы вызываете открытый метод для класса, который не влияет на состояние экземпляра этого класса, он ДОЛЖЕН быть статическим, чтобы это было понятно клиентскому разработчику. Если этот метод делает так много, что ему нужно издеваться, это другая проблема, которую можно решить разными способами.
pdr
4

Простейшее объяснение --- Статический => В каждой среде будет существовать только одна копия.

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

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

Джеймс Андерсон
источник
2

Статические члены связаны с классом, а не с каким-либо экземпляром этого класса.

Поскольку мы говорим о .Net, рассмотрим класс String , в частности методы Split и Join .

Сплит является методом экземпляра . Создайте строковую переменную, присвойте ей значение, и вы можете вызвать Split () для этой переменной / значения и получить массив «битов»:

String s1 = "abc,def,ghi" ; 
String[] array2 = s1.Split( ',' ) ; 

Так, например, методы, значение в данном экземпляре класса имеет значение .

Присоединение - это статический метод. Хорошо, это дает Струнный результат , когда данный разделитель и массив строк , чтобы жевать, так что это «что - то делать с» класса струнных, но это не связано с какой - либо конкретной величины в любом случае String ( на самом деле, значения экземпляра являются недоступно для статических методов).
В других языках метод Join мог «застрять» в классе Array (или, возможно, лучше в классе StringArray), но наши друзья в Редмонде решили, что он более «актуален» для класса String, поэтому они поместили его туда ,

String[] array3 = { ... } 
s1 = String.Join( array3, "," ) ; 

Другой альтернативой может быть использование метода Join экземпляра , в котором значение, содержащееся в String [экземпляр класса], которое мы использовали в качестве разделителя присоединения, что-то вроде:

// Maybe one day ... 
String s4 = "," ; 
s1 = s4.Join( array3 ) ; 
Фил В.
источник
2

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

Не вдаваясь в подробности, C # (и Java) жестко реализуют объектно-ориентированный идеал, согласно которому весь код и данные должны принадлежать объекту, и, следовательно, ограничены по объему, видимости и времени жизни. Это, как правило, лучшая практика, когда применяется фундаментальный принцип объекта, представляющего какую-то реальную вещь. Однако, это не всегда; иногда вам нужна функция или переменная, к которой вы можете получить доступ из любого места в коде, без необходимости передавать ссылку на объект, содержащий ее, и с гарантией того, что данные, которые вы просматриваете или изменяете, - это то , что все иначе имеет дело, а не копия этого, принадлежащая другому экземпляру объекта.

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

Любой «элемент кода» (функция, свойство, поле), объявленный как, staticвходит в область действия с первой строки функции программы main()и не покидает ее, пока main()функция не завершится. Говоря простым языком, статический член существует и может использоваться, пока работает программа. Кроме того, статические члены вызываются путем вызова их как членов самого типа, а не членов какого-либо одного экземпляра этого типа:

public class Foo
{
   public int MyInt {get;set;} //this is an "instance member"
   public static int MyStaticInt {get;set;} //this is a "static member"
}

...

var myFoo = new Foo();
myFoo.MyInt = 5; //valid
myFoo.MyStaticInt = 5; //invalid; MyStaticInt doesn't belong to any one Foo

Foo.MyInt = 5; //invalid; MyInt only has meaning in the context of an instance
Foo.MyStaticInt = 2; //valid

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

Чтобы ответить на ваш вопрос, основное преимущество маркировки чего-либо как статического состоит в том, что оно становится видимым везде, где известен сам тип, независимо от того, имеет ли потребительский код или может получить экземпляр содержащего его объекта. Есть также небольшое преимущество в производительности; Поскольку метод находится в статической области видимости, он может получить доступ только к другим статическим элементам (того же класса или других) и ко всему, что передается в качестве параметра. Следовательно, среда выполнения не должна разрешать какую-либо ссылку на текущий экземпляр содержащего объекта, как это обычно должно быть для метода экземпляра, чтобы предоставлять информацию о состоянии, зависящую от контекста.

Целые классы также могут быть помечены как статические; тем самым вы сообщаете компилятору, что объявление класса будет состоять исключительно из статических членов и, следовательно, не может быть создано. Это простой способ убедиться, что в памяти есть одна и только одна копия объекта; сделать класс и все в нем статичным. Однако очень редко это лучшее решение для такой необходимости. В ситуации, когда требуется ровно одна копия набора данных, вместо этого обычно выступает «синглтон»; это нестатический класс, который использует статический метод доступа и непубличный конструктор, чтобы обеспечить доступ к одному его экземпляру. Теоретически, синглтон обеспечивает почти те же преимущества полностью статического класса, но с добавленной возможностью использовать этот класс объектно-ориентированным способом.

Keiths
источник