c# - tablas - optimizar consultas lentas mysql
No entiendo por qué necesitamos la palabra clave ''nueva'' (6)
Aquí hay muchos conceptos erróneos, tanto en la pregunta en sí misma como en las varias respuestas.
Permítanme comenzar examinando la premisa de la pregunta. La pregunta es "¿por qué necesitamos la new
palabra clave en C #?" La motivación para la pregunta es este fragmento de C ++:
MyClass object; // this will create object in memory
MyClass* object = new MyClass(); // this does same thing
Critico esta pregunta por dos motivos.
En primer lugar, estos no hacen lo mismo en C ++ , por lo que la pregunta se basa en una comprensión errónea del lenguaje C ++. Es muy importante entender la diferencia entre estas dos cosas en C ++ , de modo que si no comprende muy bien cuál es la diferencia, busque un mentor que pueda enseñarle cómo saber cuál es la diferencia y cuándo usar cada una.
Segundo, la pregunta presupone, incorrectamente, que esas dos sintaxis hacen lo mismo en C ++, y luego, curiosamente, pregunta "¿por qué necesitamos algo new
en C #?" Seguramente la pregunta correcta a la que se debe esto, una vez más, falsa, la presuposición es "¿por qué necesitamos algo new
en C ++?" Si esas dos sintaxis hacen lo mismo, cosa que no hacen, ¿por qué tienen dos sintaxis en primer lugar?
Entonces, la pregunta se basa en una premisa falsa, y la pregunta sobre C # no se sigue del diseño - incomprendido - de C ++.
Esto es un desastre. Vamos a descartar esta pregunta y hacer algunas preguntas mejores. Y formulemos la pregunta sobre C # qua C #, y no en el contexto de las decisiones de diseño de C ++.
¿Qué hace el
new X
operadornew X
en C #, donde X es una clase o tipo de estructura? (Vamos a ignorar delegados y matrices para los fines de esta discusión).
El nuevo operador:
- Hace que se asigne una nueva instancia del tipo dado; las nuevas instancias tienen todos sus campos inicializados a los valores predeterminados.
- Hace que se ejecute un constructor del tipo dado.
- Produce una referencia al objeto asignado, si el objeto es un tipo de referencia, o el valor en sí mismo si el objeto es un tipo de valor.
De acuerdo, ya puedo escuchar las objeciones de los programadores de C #, así que ignorémoslas.
Objeción: no se asigna ningún nuevo almacenamiento si el tipo es un tipo de valor, te oigo decir. Bueno, la especificación C # no está de acuerdo contigo. Cuando tu dices
S s = new S(123);
para algunos struct tipo S
, la especificación dice que el nuevo almacenamiento temporal se asigna en el grupo de corto plazo, se inicializa con sus valores predeterminados, el constructor se ejecuta con this
conjunto para hacer referencia al almacenamiento temporal y luego el objeto resultante se copia a s
. Sin embargo, el compilador puede usar una optimización de copia-elisión siempre que pueda demostrar que es imposible que la optimización se observe en un programa seguro. (Ejercicio: determine bajo qué circunstancias no se puede realizar una elisión de copia; proporcione un ejemplo de un programa que tendría diferentes comportamientos si se usó o no la elisión).
Objeción: una instancia válida de un tipo de valor se puede producir usando el default(S)
; no se llama a ningún constructor, te oigo decir. Eso es correcto. No dije que lo new
es la única forma de crear una instancia de un tipo de valor.
De hecho, para un tipo de valor, las new S()
y las default(S)
son la misma cosa.
Objeción: ¿Es realmente un constructor ejecutado para situaciones como la new S()
, si no está presente en el código fuente en C # 6, te oigo decir? Este es un "si un árbol cae en el bosque y nadie lo oye, ¿hace un sonido?" pregunta. ¿Hay alguna diferencia entre una llamada a un constructor que no hace nada y ninguna llamada? Esta no es una pregunta interesante. El compilador puede eludir llamadas que sabe que no hace nada.
Supongamos que tenemos una variable de tipo de valor. ¿Debemos inicializar la variable con una instancia producida por una
new
?
No. Las variables que se inicializan automáticamente , como campos y elementos de matriz, se inicializarán al valor predeterminado, es decir, el valor de la estructura donde todos los campos son en sí mismos sus valores predeterminados.
Los parámetros formales se inicializarán con el argumento, obviamente.
Se requiere que las variables locales del tipo de valor sean definitivamente asignadas con algo antes de que se lean los campos, pero no tiene que ser una expresión new
.
Entonces, de manera efectiva, las variables del tipo de valor se inicializan automáticamente con el equivalente del
default(S)
, a menos que sean locales.
Sí.
¿Por qué no hacer lo mismo para los lugareños?
El uso de un local no inicializado está fuertemente asociado con el código con errores. El lenguaje C # no permite esto porque al hacerlo encuentra errores.
Supongamos que tenemos una variable de tipo de referencia. ¿Debemos inicializar
S
con una instancia producida pornew
?
No. Las variables de inicialización automática se inicializarán con nulo. Los locales se pueden inicializar con cualquier referencia, incluido null
, y deben asignarse definitivamente antes de ser leídos.
Entonces, efectivamente, las variables de tipo de referencia se inicializan automáticamente con
null
, a menos que sean locals?
Sí.
¿Por qué no hacer lo mismo para los lugareños?
Misma razón. Un posible error.
¿Por qué no inicializar automáticamente las variables de tipo de referencia llamando automáticamente al constructor predeterminado? Es decir, ¿por qué no hacer
R r;
lo mismo queR r = new R();
?
Bueno, antes que nada, muchos tipos no tienen un constructor predeterminado o, para el caso, ningún constructor accesible . Segundo, parece extraño tener una regla para un campo o campo no inicializado, otra regla para una regla formal y otra para un elemento de matriz. En tercer lugar, la regla existente es muy simple: una variable debe inicializarse a un valor; ese valor puede ser lo que quieras; ¿por qué la suposición de que se desea una nueva instancia se justifica ? Sería extraño si esto
R r;
if (x) r = M(); else r = N();
hizo que un constructor se ejecutara para inicializar r
.
Dejando de lado la semántica del
new
operador, ¿por qué es necesario tener dicho operador sintácticamente ?
No es. Hay cualquier cantidad de sintaxis alternativas que podrían ser gramaticales. Lo más obvio sería simplemente eliminar completamente lo new
. Si tenemos una clase C
con un constructor C(int)
, podríamos simplemente decir C(123)
lugar de new C(123)
. O podríamos usar una sintaxis como C.construct(123)
o algo así. Hay varias maneras de hacerlo sin el new
operador.
¿Entonces por qué lo tienes?
En primer lugar, C # se diseñó para ser inmediatamente familiar para los usuarios de C ++, Java, JavaScript y otros lenguajes que usan los new
para indicar que se está inicializando un nuevo almacenamiento para un objeto.
Segundo, el nivel correcto de redundancia sintáctica es altamente deseable. La creación de objetos es especial; deseamos llamar cuando sucede con su propio operador.
Soy nuevo en C #, desde un fondo C ++. En C ++ puedes hacer esto:
class MyClass{
....
};
int main()
{
MyClass object; // this will create object in memory
MyClass* object = new MyClass(); // this does same thing
}
Mientras que, en C #:
class Program
{
static void Main(string[] args)
{
Car x;
x.i = 2;
x.j = 3;
Console.WriteLine(x.i);
Console.ReadLine();
}
}
class Car
{
public int i;
public int j;
}
no puedes hacer esto Me pregunto por qué Car x
no hará su trabajo.
Cuando usa tipos referenciados, entonces en esta declaración
Car c = new Car();
se crean dos entidades: una referencia llamada c
a un objeto de tipo Car en la pila y el objeto de tipo Car en el montón.
Si solo escribes
Car c;
luego creas una referencia no inicializada (siempre que c
sea una variable local) que apunta a ninguna parte.
De hecho, es equivalente al código C ++ donde en lugar de referencias se usan punteros.
Por ejemplo
Car *c = new Car();
o solo
Car *c;
La diferencia entre C ++ y C # es que C ++ puede crear instancias de clases en la pila como
Car c;
En C # esto significa crear una referencia de tipo Car que, como dije, no apunta a ninguna parte.
De la guía de programación de microsoft:
En tiempo de ejecución, cuando declara una variable de un tipo de referencia, la variable contiene el valor nulo hasta que cree explícitamente una instancia del objeto utilizando el nuevo operador, o le asigna un objeto que ha sido creado en otro lugar mediante el uso de nuevo
Una clase es un tipo de referencia. Cuando se crea un objeto de la clase, la variable a la que está asignado el objeto solo contiene una referencia a esa memoria. Cuando la referencia del objeto se asigna a una nueva variable, la nueva variable se refiere al objeto original. Los cambios realizados a través de una variable se reflejan en la otra variable porque ambos se refieren a los mismos datos.
Una estructura es un tipo de valor. Cuando se crea una estructura, la variable a la que se asigna la estructura contiene los datos reales de la estructura. Cuando la estructura se asigna a una nueva variable, se copia. La nueva variable y la variable original por lo tanto contienen dos copias separadas de los mismos datos. Los cambios realizados en una copia no afectan la otra copia.
Creo que en tu ejemplo C # intentas asignar valores a un puntero nulo. En la traducción c ++ esto se vería así:
Car* x = null;
x->i = 2;
x->j = 3;
Obviamente, esto se compilaría pero colapsaría.
En C # puedes hacer lo mismo:
// please notice "struct"
struct MyStruct {
....
}
MyStruct sample1; // this will create object on stack
MyStruct sample2 = new MyStruct(); // this does the same thing
Recuerde que las primitivas como int
, double
y bool
también son de tipo struct
, por lo que aunque es convencional escribir
int i;
también podemos escribir
int i = new int();
a diferencia de C ++, C # no usa punteros (en el modo seguro) para las instancias, sin embargo C # tiene declaraciones de class
y struct
:
class
: tiene referencia a la instancia, la memoria se asigna en el montón , lanew
es obligatoria ; similar aMyClass*
en C ++struct
: tiene valor , la memoria está (usualmente) asignada en la pila , lanew
es opcional ; similar aMyClass
en C ++
En tu caso particular, puedes simplemente convertir Car
en struct
struct Car
{
public int i;
public int j;
}
y entonces el fragmento
Car x; // since Car is struct, new is optional now
x.i = 2;
x.j = 3;
será correcto
En C #, class
objetos de tipo de class
siempre se asignan en el montón, es decir, las variables de dichos tipos siempre son referencias ("punteros"). El solo hecho de declarar una variable de ese tipo no causa la asignación de un objeto. Asignar un objeto de class
en la pila como es común en C ++ no es (en general) una opción en C #.
Las variables locales de cualquier tipo que no se hayan asignado se consideran no inicializadas y no se pueden leer hasta que se hayan asignado. Esta es una opción de diseño (otra forma habría sido asignar un default(T)
a cada variable en el momento de la declaración), lo que parece una buena idea, ya que debería protegerlo de algunos errores de programación.
Es similar a cómo en C ++ no tendría sentido decir el SomeClass *object;
y nunca le asignen nada
Como en C # todas class
variables de tipo de class
son punteros, asignar un objeto vacío cuando se declara la variable conduciría a un código ineficiente cuando en realidad solo desee asignar un valor a la variable más adelante, por ejemplo en situaciones como esta:
// Needs to be declared here to be available outside of `try`
Foo f;
try { f = GetFoo(); }
catch (SomeException) { return null; }
f.Bar();
O
Foo f;
if (bar)
f = GetFoo();
else
f = GetDifferentFoo();
ignorando el lado de la pila frente al montón de cosas:
porque C # tomó la mala decisión de copiar C ++ cuando deberían haber hecho la sintaxis
Car car = Car()
(o algo similar). Tener ''nuevo'' es superfluo.