Não é possível usar uma matriz “inline” em C #?


89

Imagine que você tem isso em algum lugar

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

ou mesmo só isso

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

Então, é claro que você pode fazer isso ...

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

... o que é ótimo. MAS. Parece que você NÃO pode fazer isso:

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

ou talvez isso

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

ou qualquer outra coisa que eu tentei.

Na verdade (1) por que não podemos fazer isso? e (2) estou faltando alguma coisa, como você faria isso se houvesse uma maneira?


5
Não tenho certeza se a pergunta duplicada é apropriada, o OP não está perguntando sobre inicializadores de array, mas por que o compilador não reconhece o objeto como um array até que seja atribuído.
Ron Beyer

4
Não estou familiarizado com a terminologia C #, mas acredito que isso seja mais comumente chamado de literal ou literal de array em vez de embutido .
chi

3
Esse elemento sintático é um inicializador de array ou inicializador de coleção , dependendo do contexto em que é usado. Em nenhum dos casos é classificado como expressão .
Eric Lippert

Respostas:


129

Você tem que criar a matriz primeiro, usando new[].

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

Como @hvd mencionou, você pode fazer isso sem parênteses (..), adicionei os parênteses porque acho que é mais legível.

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

E você pode especificar o tipo de dados new string[]como em outras respostas foi mencionado.


Você não pode simplesmente fazer {"a","b","c"}, porque você pode pensar nisso como uma forma de preencher o array, não de criá-lo.

Outra razão será que o compilador ficará confuso, não saberá o que criar, por exemplo, a string[]{ .. }ou a List<string>{ .. }.

Usando apenas o new[]compilador pode saber por tipo de dados ( ".."), entre {..}, o que você quer ( string). A parte essencial é []que isso significa que você deseja um array.

Você não pode nem mesmo criar um array vazio com new[].

string[] array = new []{ }; // Error: No best type found for implicity-typed array

13
Você não precisa desses parênteses. string letter = new[] {"a","b","c"}.AnyOne();está muito bem. Se você quiser, se achar que fica mais legível com os parênteses, eles são válidos, mas nesse caso acho que vale a pena mencionar que é uma escolha consciente de sua parte, que não foi forçado pela linguagem.

Eu sei sobre a sintaxe new [] {1,2}, mas existe uma sintaxe ainda mais simples? Algo como [1, 2]?
seguso

51

(1) por que não podemos fazer isso? {"a","b","c"}.AnyOne();

Está linha:

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

é uma abreviação para uma expressão de criação de array equivalente (em ILSpy )

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

Isso string[] st = {"a","b","c"} só pode ser usado no momento da declaração , você não pode não usar em outro lugar, nem pode fazer:

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

Isso é explicado na Seção 7.6.10.4 para expressão de criação de matriz em especificações de linguagem C #.

Portanto, isso "{"a", "b", "c"}"sozinho, sem uso na declaração, não significa nada. Portanto, você não pode usá-lo com seu método de extensão, pois seu método de extensão opera em um array.

(2) estou faltando alguma coisa, como você faria isso se houvesse uma maneira?

Já mencionado na resposta de @adricadar , você pode fazer:

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

ou

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

48

Eu recuo nas perguntas "por que não" porque, primeiro, as respostas quase nunca são satisfatórias - você já obteve a resposta "o recurso não está do jeito que você deseja porque a especificação não diz o que você quer dizer" , o que eu imagino não foi uma resposta particularmente satisfatória. Em segundo lugar, a equipe de design não precisa justificar por que o mundo não é como você gostaria que fosse; os recursos não existem gratuitamente e são projetados fora da linguagem; em vez disso, os recursos devem ser primeiro justificados e, em seguida, projetados em.

Portanto, vamos tentar tornar sua pergunta "por que não" um pouco mais nítida. O recurso existente é "um inicializador de array pode ser usado (a) no lado direito dos iguais em uma inicialização ou (b) à direita de uma construção de objeto do tipo array." O recurso proposto é: "um inicializador de array também pode ser usado como uma expressão". A questão é "que críticas Eric faria ao recurso proposto?"

A primeira crítica que faço é que não está claro qual é o tipo de expressão. Em um inicializador de variável você tem o tipo da variável e em uma expressão de criação de objeto você tem o tipo do objeto; de ambos, podemos deduzir o tipo do array construído. Sem nenhuma dica, que tipo devemos deduzir?

No C # 1.0, quando esse recurso foi adicionado, havia um total geral de inferências de tipo zero feitas na linguagem. Um princípio de design nos primeiros dias do C # era "sem surpresas" e que o compilador não era "muito inteligente". Se o desenvolvedor pretende que uma expressão seja de um tipo específico, esse tipo deve ser de alguma forma óbvio na expressão. Quando voce diz

new double[] { 1, 2, 3.4 }

é bastante claro que tipo se destina. similarmente

new Animal[] { cat, dog, null }

O recurso proposto viola esse princípio. A expressão deve ter um tipo, mas não está claro qual é o tipo do argumento

M({cat, dog, null})

Além disso: suponha que temos duas sobrecargas de M, uma das quais obtém um array de Animale a outra obtém um array de IPet. Qual sobrecarga de Mé aplicável? Uma das conversões é melhor que a outra? Os tipos dos elementos são Cate Dog; faz sentido deduzir um tipo que nem mesmo aparece lá? Todas essas são perguntas que devem ser consideradas pela equipe de design, e essas são perguntas que não têm respostas óbvias. O recurso proposto nos leva a águas profundas em um curto espaço de tempo.

Agora, o C # 3.0 resolve esse problema porque o C # 3.0 adicionou vários recursos nos quais o compilador infere tipos em nome do desenvolvedor. Os princípios anteriores sobre "sem surpresas" e "regras simples" estavam em conflito com outros princípios de design necessários para fazer o LINQ funcionar. O recurso que você propõe deveria ter sido adicionado ao C # 3.0?

Poderia ter sido. O recurso realmente adicionado no C # 3.0 foi:

new[] { x, y, z }

infere o tipo da matriz usando o algoritmo: pegue as expressões para elementos que possuem tipos, determine qual desses tipos é o tipo mais geral exclusivo para o qual todas as outras expressões são conversíveis e, se esse tipo existir, escolha esse tipo. Caso contrário, produza um erro,

Esse recurso poderia ter sido mais relaxado para tornar o new[]opcional. Isso não foi feito.

Agora, se você tivesse me pedido no intervalo de tempo do C # 3.0 para criticar o recurso proposto, eu teria apontado que (1) o compilador C # 3.0 já estava em sério risco de perder o cronograma de todo o lançamento, então não vamos adicionar mais nada carga de projeto, implementação e teste para um recurso totalmente desnecessário que economiza seis pressionamentos de tecla para o usuário e (2) C # 3.0 também adicionou inicializadores de coleção:

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

por que deveria {10, 20, 30}ser automaticamente uma matriz ? Por que não deveria ser List<int>? Ou qualquer um de vários outros tipos? Por que o preconceito em relação a matrizes? Lembre-se, uma vez que escolhemos consagrar a sintaxe para matrizes, ficaremos presos a ela para sempre . Pode nunca ser outra coisa , então o recurso proposto não é apenas desnecessário, mas também evita possíveis recursos futuros que parecem plausíveis.

Resumindo: o recurso proposto violou diretamente alguns dos princípios de design do C # 1.0. Ele adiciona nada além de uma carga desnecessária ao C # 3.0. Em todas as versões da linguagem desde C # 3.0, o recurso proposto não tem nenhum bom argumento para recomendar o gasto de tempo, esforço e dinheiro com ele em vez de muitos outros recursos mais valiosos.

Portanto, esse recurso não existe.


Ei, você precisa ter uma camiseta impressa com "o mundo não é do jeito que você gostaria que fosse" impresso nela :)
slugster

6
@JoeBlow: Em primeiro lugar, você é muito bem-vindo. Quanto ao "por que não" - seu comentário ilustra bem o problema. Quando algumas pessoas fazem uma pergunta "por que", estão procurando uma justificativa lógica . Algumas pessoas procuram uma justificação pragmática . E aparentemente você está procurando a linha da especificação que descreve a regra . É tão vago que é muito difícil criar uma boa resposta que tenha como alvo a pergunta na mente de quem está fazendo a pergunta. Perguntas "por que não" são ainda piores porque são perguntas vagas sobre coisas que nem existem .
Eric Lippert

3
@EricLippert É ainda pior do que apenas a questão sobre coisas que _podem_ existir : se você está trabalhando em um software com uma equipe, a equipe inteira passa todos os dias do ano pensando nos recursos e equilibrando as consequências. As decisões são tomadas. Solicitações de recursos e 'por que' pede justificativa. No entanto, porque não implica que alguém apenas deseja algo feito, o que basicamente questiona o julgamento da própria equipe. Pior ainda, a pessoa que faz a pergunta por que não geralmente sabe pouco sobre o assunto. Como tal, acho que empurrar para trás nas perguntas por que não é totalmente justo. Bom trabalho IMO.
atlaste
Ao utilizar nosso site, você reconhece que leu e compreendeu nossa Política de Cookies e nossa Política de Privacidade.
Licensed under cc by-sa 3.0 with attribution required.