No C# 12, há o analisador "A inicialização da coleção pode ser simplificada" IDE0028 .
Estou me perguntando como o fixador decide quando corrigir ou não inicializadores como new()
, new List<string>()
, new Collection<int>()
, Array.Empty<double>()
, new string[] { }
, etc. A correção é substituir um inicializador qualificado por []
.
Por exemplo:
// Collection initialization can be simplified
public List<string> Foo1 { get; } = new List<string>();
public HashSet<string> Foo2 { get; } = new HashSet<string>();
public ICollection<string> Foo3 { get; } = new List<string>();
public Dictionary<int, string> Foo4 = new Dictionary<int, string>();
// Collection initialization can be simplified (may change semantics)
public IEnumerable<string> Bar1 { get; } = new List<string>();
public ICollection<string> Bar2 { get; } = new Collection<string>();
// no code fix offered
public IDictionary<int, string> Baz1 = new Dictionary<int, string>();
Geralmente escolho um tipo de coleção que se adapta a um propósito específico. Então, não quero aceitar cegamente a recomendação do analisador sem entender qual tipo de coleção o compilador realmente usará quando vir []
.
Então:
- Como o analisador/fixador decide quando sugerir uma correção e o que exatamente significa "pode alterar a semântica"
- Que tipo de coleção o compilador/tempo de execução realmente usa quando encontra
[]
"Pode alterar a semântica" significa apenas que usar um literal de coleção aqui não produz código equivalente ao código que você tem atualmente.
As regras para determinar qual tipo usar são especificadas nas seções Construção e Tradução Literal de Coleção da especificação do recurso.
Para classes e estruturas concretas (sem um método create ), o literal de coleção criará uma instância dessa classe/estrutura usando seu construtor sem parâmetros e, em seguida, adicionará os elementos.
Como as coisas são adicionadas à coleção também segue algumas regras, mas elas não são relevantes aqui porque não há elementos em nenhum dos literais da coleção. Leia a especificação se estiver interessado.
Isso explica por que os casos
Foo1
,Foo2
eFoo4
não alteram a semântica.Para interfaces, a especificação dá relativamente mais liberdade. Para uma interface somente leitura como
IEnumerable<T>
ouIReadOnlyList<T>
, o compilador é livre para usar qualquer tipo concreto que preencha alguns requisitos.Portanto, o
Bar1
caso diz "pode mudar a semântica". O literal da coleção é, na verdade, traduzido paraArray.Empty()
under the hood.Para interfaces mutáveis como
ICollection<T>
, é especificado que o literal de coleção sempre será umList<T>
, então oFoo3
caso não diz "pode alterar a semântica", masBar2
sim.Por fim, literais de coleção simplesmente não suportam
IDictionary<K, V>
, então não há diagnóstico paraBaz1
. De todas as interfaces, literais de coleção só funcionam comIEnumerable<T>
,IReadOnlyCollection<T>
,IReadOnlyList<T>
,ICollection<T>
eIList<T>
.