¿No puede usar una matriz "en línea" en C #?

92

Imagina que tienes esto en alguna parte

public static T AnyOne<T>(this T[] ra) where T:class
    {
    int k = ra.Length;
    int r = Random.Range(0,k);
    return ra[r];
    }

o incluso solo esto

public static string OneOf(this string[] strings)
    {
    return "a";
    }

Entonces, por supuesto que puedes hacer esto ...

string[] st = {"a","b","c"};
string letter = st.AnyOne();

... Lo cual es genial. PERO. Parece que NO puede hacer esto:

string letter = {"a","b","c"}.AnyOne();

o de hecho tal vez esto

string letter = ( {"a","b","c"} ).AnyOne();

o cualquier otra cosa que probé.

De hecho (1) ¿por qué no se puede hacer eso? y (2) me estoy perdiendo algo, ¿cómo harías eso si hubiera una manera?

Fattie
fuente
5
No estoy seguro de que la pregunta duplicada sea apropiada, el OP no pregunta sobre los inicializadores de matriz, sino por qué el compilador no reconocerá el objeto como una matriz hasta que se asigne.
Ron Beyer
4
No estoy familiarizado con la terminología de C #, pero creo que esto se denomina más comúnmente literal o literal de matriz en lugar de en línea .
chi
3
Ese elemento sintáctico es un inicializador de matriz o un inicializador de colección , según el contexto en el que se utilice. En ninguno de los casos se clasifica como expresión .
Eric Lippert

Respuestas:

133

Primero tienes que crear la matriz, usando new[].

string letter = (new[] {"a","b","c"}).AnyOne();

Como @hvd mencionó, puede hacer esto sin paréntesis (..), agregué los paréntesis porque creo que es más legible.

string letter = new[] {"a","b","c"}.AnyOne();

Y puede especificar el tipo de datos new string[]como se ha mencionado en otras respuestas.


No puede simplemente hacer {"a","b","c"}, porque puede pensar en ello como una forma de completar la matriz, no de crearla.

Otra razón será que el compilador estará confundido, no sabrá qué crear, por ejemplo, a string[]{ .. }o a List<string>{ .. }.

Usando solo el new[]compilador puedes saber por tipo de datos ( ".."), entre {..}, lo que quieres ( string). La parte esencial es []que eso significa que desea una matriz.

Ni siquiera puedes crear una matriz vacía con new[].

string[] array = new []{ }; // Error: No best type found for implicity-typed array
adricadar
fuente
13
No necesitas esos paréntesis. string letter = new[] {"a","b","c"}.AnyOne();está bien. Si los quieres, si crees que es más legible entre paréntesis, son válidos, pero en ese caso creo que al menos vale la pena mencionar que es una elección consciente de tu parte, que no fue forzada por el idioma.
Conozco la sintaxis new [] {1,2}, pero ¿hay una sintaxis aún más simple? ¿Algo como [1, 2]?
seguso
51

(1) ¿Por qué no se puede hacer eso? {"a","b","c"}.AnyOne();

Esta línea:

string[] st = {"a","b","c"};

es una abreviatura de una expresión de creación de matriz equivalente (en ILSpy )

string[] st = new string[]  {"a","b","c"};

Esto string[] st = {"a","b","c"} solo se puede usar en el momento de la declaración , no puede no usarlo en otro lugar, ni siquiera puede hacer:

string[] st;
st = {"a", "b", "c"}; //Error

Se explica en la Sección 7.6.10.4 para la expresión de creación de matrices en las especificaciones del lenguaje C #.

Entonces, esto "{"a", "b", "c"}"solo sin uso en la declaración no significa nada. Por lo tanto, no puede usarlo con su método de extensión, ya que su método de extensión opera en una matriz.

(2) ¿Me estoy perdiendo algo? ¿Cómo harías eso si hubiera una manera?

Ya mencionado en la respuesta de @ adricadar , puede hacer:

(new[] {"a","b","c"}).AnyOne();

o

(new string[] {"a","b","c"}).AnyOne();
Habib
fuente
48

Rechazo las preguntas del tipo "por qué no" porque, en primer lugar, las respuestas casi nunca son satisfactorias; ya has obtenido la respuesta "la función no es la que quieres porque la especificación no dice lo que quieres que diga". , que imagino que no fue una respuesta particularmente satisfactoria. En segundo lugar, el equipo de diseño no tiene que justificar por qué el mundo no es como usted desea que sea; las funciones no existen de forma gratuita y luego se diseñan fuera del idioma; más bien, las características deben justificarse primero y luego diseñarse.

Así que intentemos hacer su pregunta de "por qué no" un poco más clara. La característica existente es "se puede usar un inicializador de matriz (a) en el lado derecho de los iguales en una inicialización o (b) a la derecha de una construcción de objeto de tipo matriz". La característica propuesta es: "un inicializador de matriz también se puede utilizar como expresión". La pregunta es "¿qué críticas haría Eric sobre la función propuesta?"

La primera crítica que haría es que no está claro cuál es el tipo de expresión. En un inicializador de variable tienes el tipo de variable y en una expresión de creación de objeto tienes el tipo de objeto; de ambos podemos deducir el tipo de matriz construida. Sin ninguna pista, ¿qué tipo debemos deducir?

En C # 1.0, cuando se agregó esta característica, hubo un gran total de inferencias de tipo cero hechas en el lenguaje. Un principio de diseño en los primeros días de C # era "sin sorpresas" y que el compilador no era "demasiado inteligente". Si el desarrollador tiene la intención de que una expresión sea de un tipo particular, ese tipo debería ser de alguna manera obvio en la expresión. Cuando tu dices

new double[] { 1, 2, 3.4 }

está bastante claro qué tipo se pretende. similar

new Animal[] { cat, dog, null }

La característica propuesta viola este principio. La expresión debe tener un tipo, pero de ninguna manera está claro cuál es el tipo de argumento en

M({cat, dog, null})

Además: supongamos que tenemos dos sobrecargas de M, una de las cuales toma una matriz de Animaly otra que toma una matriz de IPet. ¿Qué sobrecarga de Mes aplicable? ¿Es una de las conversiones mejor que la otra? Los tipos de elementos son Caty Dog; ¿Tiene sentido deducir un tipo que ni siquiera aparece ahí? Todas estas son preguntas que deben ser consideradas por el equipo de diseño, y estas son preguntas que de ninguna manera tienen respuestas obvias. La característica propuesta nos lleva a aguas profundas en muy poco tiempo.

Ahora, C # 3.0 resuelve este problema porque C # 3.0 agregó numerosas características donde el compilador infiere tipos en nombre del desarrollador. Los principios anteriores sobre "sin sorpresas" y "reglas simples" estaban en conflicto con otros principios de diseño necesarios para que LINQ funcione. ¿Debería haberse agregado la característica que propone en C # 3.0?

Podría haber sido. La característica realmente agregada en C # 3.0 fue:

new[] { x, y, z }

infiere el tipo de la matriz usando el algoritmo: tome las expresiones para los elementos que tienen tipos, determine cuál de esos tipos es el tipo único más general al que todas las demás expresiones son convertibles, y si tal tipo existe, elija ese. De lo contrario producirá un error,

Esa característica podría haberse relajado aún más para convertirla en new[]opcional. Esto no se hizo.

Ahora, si me hubieras pedido en el marco de tiempo de C # 3.0 que criticara la característica propuesta, habría señalado que (1) el compilador de C # 3.0 ya estaba en grave peligro de retrasar el cronograma de toda la versión, así que no agreguemos más carga de diseño, implementación y prueba para una característica completamente innecesaria que le ahorra al usuario seis pulsaciones de teclas, y (2) C # 3.0 también agregó inicializadores de colección:

new List<int>() { 10, 20, 30 }

¿Por qué debería {10, 20, 30}ser automáticamente una matriz ? ¿Por qué no debería ser un List<int>? ¿O cualquiera de otros tipos? ¿Por qué el sesgo hacia las matrices? Recuerde, una vez que elegimos consagrar la sintaxis de las matrices, estaremos atrapados con ella para siempre . Puede que nunca sea otra cosa, por lo que la función propuesta no solo es innecesaria, sino que también evita posibles funciones futuras que parecen plausibles.

Resumiendo: la característica propuesta violó directamente algunos de los principios de diseño de C # 1.0. Agrega nada más que una carga innecesaria a C # 3.0. En todas las versiones del lenguaje desde C # 3.0, la característica propuesta no tiene un buen argumento para recomendar gastar tiempo, esfuerzo y dinero en ella sobre muchas otras características más valiosas.

Por lo tanto, no existe tal característica.

Eric Lippert
fuente
Je, necesitas imprimir una camiseta con "el mundo no es como tú quieres" impreso :)
slugster
6
@JoeBlow: Primero que nada, eres bienvenido. Con respecto a "por qué no", su comentario ilustra muy bien el problema. Cuando algunas personas hacen una pregunta de "por qué", buscan una justificación lógica . Algunas personas buscan una justificación pragmática . Y aparentemente está buscando la línea de la especificación que describe la regla . Es tan vago que es muy difícil elaborar una buena respuesta que se dirija a la pregunta que está realmente en la mente del interrogador. Las preguntas de "por qué no" son aún peores porque son preguntas vagas sobre cosas que ni siquiera existen .
Eric Lippert
3
@EricLippert Es incluso peor que la simple pregunta sobre cosas que _ pueden existir_ : si estás trabajando en software con un equipo, todo el equipo pasa todos los días del año pensando en las características y sopesando las consecuencias. Se toman decisiones. Solicitudes de funciones y "por qué" solicita justificación. Sin embargo, por qué no implica que alguien solo quiere hacer algo, lo que básicamente cuestiona el juicio del equipo en sí. Peor aún, la persona que hace la pregunta por qué no suele saber poco sobre el tema. Como tal, creo que rechazar las preguntas de por qué no es totalmente justo. Buen trabajo en mi opinión.
Atlas