Comprender la palabra clave estática

16

Tengo cierta experiencia en el desarrollo con Java, Javascript y PHP.

Estoy leyendo Microsoft Visual C # 2010 paso a paso, y creo que es un muy buen libro sobre cómo presentarle el lenguaje C #.

Parece que tengo problemas para entender la palabra clave estática. Por lo que entiendo hasta aquí, si una clase se declara estática, todos los métodos y variables tienen que ser estáticos. El método principal siempre es un método estático, por lo que en la clase en la que existe el método principal, todas las variables y métodos se declaran estáticos si tiene que llamarlos en el método principal. También he notado que para llamar a un método estático desde otra clase no es necesario crear un objeto del que pueda usar el nombre de la clase.

Pero, ¿cuál es el propósito real de la palabra clave estática? ¿Cuándo debo declarar variables estáticas y métodos?

Nistor Alexandru
fuente
44
static en C # es casi lo mismo que static en Java. Si lo entiende en Java, no debe tener ningún problema en C #
superM
Java fue mi primer lenguaje de programación y tampoco entendí este concepto allí. Solo he usado Java por un corto período de tiempo
Nistor Alexandru
En resumen: use "estática" cuando no necesite orientación de objeto, por ejemplo, solo algunos métodos o variables independientes. Declarar que una clase es estática significa poner esas funciones y variables no orientadas a objetos solo en un nombre común (espacio), el nombre de la clase.
Doc Brown

Respuestas:

15

La palabra clave 'estática' en C # se refiere a algo en la clase, o la clase misma, que se comparte entre todas las instancias de la clase. Por ejemplo, se puede acceder a un campo marcado como estático desde todas las instancias de esa clase a través del nombre de la clase.

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'
    }

}

Puedo decir honestamente que nunca he usado una clase marcada como estática con la excepción de crear métodos de extensión ( Tutorial rápido sobre métodos de extensión ).

De todos modos, hay patrones de diseño específicos para utilizar métodos estáticos, como el patrón de fábrica y el patrón singleton , pero lo importante a recordar es que los métodos y constructores estáticos no tratan con ninguna instancia específica de una clase (a menos que pase una), típicamente para hacer cálculos o para hacer una comparación entre objetos. El método "Principal" al que se refiere es siempre estático, pero para verlo desde un punto de vista diferente, consulte este artículo .

Para seguir con esto, así es como se llama la diferencia entre métodos estáticos y instanciados, campos y propiedades.

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
}

Además, consulte esta publicación para obtener una visión más profunda de las clases estáticas.

iMortalitySX
fuente
18

Aquí está la forma de explicarlo de Joshua Bloch, que encuentro brillante como la mayoría de lo que dice (sí, soy un fanático de Joshua Bloch :)). Esto se cita de memoria.

Imagine que una clase es el equivalente de un plano para una casa. Imagínese entonces que una casa está en el plano como una instancia de la clase es para la clase. Puede tener una clase (blue-print) y múltiples instancias (casas) creadas a partir de ella.

Ahora, el sentido común dicta que la mayoría de las funciones / comportamientos que una casa (instancia) puede tener / hacer, a pesar de que se declaren en el plano, no se pueden usar hasta que una casa (instancia) real esté hecha de ese azul -print (clase). Por ejemplo, su plano puede contener el lugar donde deben ir las luces y las bombillas, pero no tiene forma de hacer que funcionen en el plano, debe construir la casa para poder para encender y apagar el interruptor de la luz y encender y apagar ciertas bombillas.

Sin embargo, es posible que tenga un comportamiento que sea aplicable directamente al plano y que pueda usar / acceder directamente en el plano sin necesidad de hacer una casa real con ese plano. Imagine que su plano tiene un botón que, al presionarlo, mostrará la huella de la casa contenida en ese plano (calculando todas las longitudes de las paredes y demás). Obviamente, PODRÍAS construir una casa primero y luego medir midiendo su huella, pero puedes hacerlo solo con el plano, por lo que sería más útil tener este comportamiento implementado en el plano. Tal botón incrustado de impresión azul que calcula la huella de la casa es el equivalente a tener una función estática en una clase.

Shivan Dragon
fuente
O tendría una Blueprintclase que implementa la funcionalidad del plano, incluida la capacidad de calcular la huella de la casa expresada por el plano. Esta instancia de plan se alimenta a una Builder(a su vez, probablemente una instancia), que a su vez hace lo necesario para construir y generar un número potencialmente arbitrario de Buildinginstancias basado en un plan.
un CVn
12

Mirándolo de esta manera me ayuda:

  • Cada tipo tiene una instancia estática.
  • La instancia estática se crea la primera vez que accede al tipo, ya sea a través de la instancia estática o creando otra instancia.
  • Puede crear tantas instancias no estáticas como desee, pero solo hay una instancia estática.
  • Cualquier cosa dentro de una clase que se declare como estática pertenece a la instancia estática y, por lo tanto, no tiene acceso a ninguna otra instancia que cree. Pero las otras instancias sí tienen acceso a la instancia estática.
  • Si una clase se declara como estática, no puede crear otras instancias, solo puede existir la instancia estática.
  • Puede declarar un constructor estático para la instancia estática como un constructor para una instancia normal (pero declarándolo estático).

En cuanto a cuándo usar la palabra clave estática:

  • Cualquier método que no necesite acceso a las propiedades locales puede y probablemente debería declararse estático.
  • Las clases auxiliares que no tienen ningún estado (lo que debería ser raro de todos modos) y que nunca serán objeto de burla pueden declararse estáticas. Si deberían hacerlo es otro asunto; use esta funcionalidad con moderación.
  • Las propiedades y los campos a los que deben acceder todas las instancias de una clase deben declararse estáticos. Pero use esto solo cuando no haya otra opción.
pdr
fuente
+1, para un buen resumen, no sabía que cada tipo tiene 1 instancia estática y me resulta extraño decirte la verdad.
NoChance
2
@EmmadKareem Ese es solo un modelo mental que usa pdr, porque "Looking at it this way helps"él. Lo encuentras extraño porque no es exactamente cierto, pero puedes pensarlo así si quieres. ¿Conoces el modelo Bohr? Es un conjunto de reglas e ideas sobre cómo los átomos y los electrones interactúan entre sí. El modelo funciona según lo que hagas, pero no es la realidad.
phant0m
@ phant0m, gracias por la explicación, tenía la impresión de que no era un modelo real y me sorprendió por eso.
NoPuerto
En realidad, a veces hay razones por las que es posible que no desee hacer un método staticincluso si no utiliza propiedades locales. Hacer cosas staticpuede agregar acoplamiento a los clientes porque tienen que abordar la clase directamente. Por ejemplo, esto puede dificultar la prueba unitaria con burla.
Allan
@Allan: Podría decirse que si está llamando a un método público en una clase que no afecta el estado de una instancia de esa clase, DEBE ser estático, para dejarlo claro al desarrollador del cliente. Si ese método hace tanto que necesita burlarse, ese es un problema diferente que se puede resolver de varias maneras diferentes.
pdr
4

La explicación más simple --- Estática => Solo existirá una copia por entorno.

Por lo tanto, dentro de una VM o CLR solo habrá una copia de una clase estática y cualquier otra clase que haga referencia a ella tendrá que compartir sus métodos y datos con todas las demás clases que la hagan referencia.

Para una variable estática, solo habrá una instancia de esta variable en el entorno de tiempo de ejecución, sin importar cuántas copias de la clase propietaria se creen cuando hagan referencia a una variable estática, todas harán referencia a la misma pieza de almacenamiento.

James Anderson
fuente
2

Los miembros estáticos están asociados con la Clase, no con ninguna instancia de esa Clase.

Como estamos hablando de .Net, considere la clase String , en particular los métodos Split y Join .

Split es un método de instancia . Cree una variable de cadena, asígnele un valor y puede llamar a Split () en esa variable / valor y recuperar una matriz de "bits":

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

Entonces, por ejemplo, los métodos, el valor contenido dentro de la instancia de clase dada es importante .

Unirse es un método estático . OK, se produce una cadena resultado cuando se les da un delimitador y una matriz de cadenas para masticar, por lo que es "algo que ver con" la clase String, pero es no asociado a ningún particular, el valor de ninguna instancia String (de hecho, valores de instancia son no disponible para métodos estáticos).
En otros idiomas, el método Join podría haberse "pegado" a la clase Array (o, quizás mejor, a una clase StringArray) pero Nuestros amigos en Redmond decidieron que era más "relevante" para la clase String, por lo que lo pusieron allí .

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

Otra alternativa podría haber sido tener un método Join de instancia , donde el valor contenido dentro de String [instancia de clase] usamos como delimitador de unión, algo así como:

// Maybe one day ... 
String s4 = "," ; 
s1 = s4.Join( array3 ) ; 
Phill W.
fuente
2

La staticpalabra clave puede ser un poco difícil de entender para los novatos. Su propósito principal es identificar a un miembro de la clase como no perteneciente a una sola instancia de la clase, sino a la clase misma.

Sin entrar en demasiados detalles, C # (y Java) imponen rígidamente el ideal orientado a objetos de que todo el código y los datos deben pertenecer a un objeto y, por lo tanto, su alcance, visibilidad y vida útil son limitados. En general, esa es la mejor práctica donde se aplica el principio fundamental de un objeto que representa algo del mundo real. Sin embargo, no siempre es así; a veces lo que necesita es una función o variable a la que pueda acceder desde cualquier parte del código, sin requerir que pase una referencia a un objeto que lo contiene, y con la garantía de que los datos que está viendo o cambiando es exactamente lo que todos más está tratando, y no una copia del mismo que pertenece a una instancia diferente de un objeto.

Tal comportamiento estaba disponible en C y C ++ en la forma de la función o variable "global", que no estaba encapsulada en un objeto. Por lo tanto, como compromiso, C # y Java admiten "alcance estático", un punto intermedio entre el código verdaderamente global sin objeto principal y los miembros de instancia de alcance limitado.

Cualquier "miembro de código" (función, propiedad, campo) declarado como staticentra en el alcance a partir de la primera línea de la main()función del programa , y no lo abandona hasta main()que finaliza la función. En inglés simple, existe un miembro estático y se puede usar mientras el programa se esté ejecutando. Además, los miembros estáticos se invocan llamándolos como miembros del tipo en sí, no como miembros de ninguna instancia de ese tipo:

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

Esto hace que los miembros estáticos sean visibles para cualquier código que tenga conocimiento del tipo, ya sea que conozcan o no alguna instancia del mismo.

Para responder a su pregunta, el beneficio principal de marcar algo como estático es que se hace visible donde se conoce el tipo en sí, independientemente de si el código consumidor tiene o puede obtener una instancia del objeto que lo contiene. También hay un ligero beneficio de rendimiento; Debido a que el método tiene un alcance estático, solo puede acceder a otros miembros estáticos (de la misma clase u otros), y todo lo que se pase como parámetro. Por lo tanto, el tiempo de ejecución no tiene que resolver ninguna referencia a la instancia actual del objeto contenedor, ya que normalmente tendría que hacerlo con un método de instancia para proporcionar información de estado específica del contexto.

Las clases enteras también se pueden marcar como estáticas; Al hacerlo, le dice al compilador que la declaración de clase consistirá únicamente en miembros estáticos y, por lo tanto, no se puede crear una instancia. Esta es una manera fácil de garantizar que haya una, y solo una, copia de un objeto en la memoria; hacer que la clase y todo lo que contiene sea estática. Sin embargo, es muy raro que esta sea la mejor solución para tal necesidad. En una situación donde se requiere exactamente una copia de un conjunto de datos, el "singleton" generalmente se recomienda en su lugar; Esta es una clase no estática, que utiliza un descriptor de acceso estático y un constructor no público para proporcionar acceso a una sola instancia de sí mismo. Teóricamente, un singleton proporciona los mismos beneficios que una clase completamente estática, pero con la capacidad adicional de usar la clase de una manera orientada a objetos y basada en instancias.

KeithS
fuente