Propriedades e estruturas automática não se misturam?
-
05-07-2019 - |
Pergunta
chutando em torno de algumas pequenas estruturas ao responder este post, me deparei com a seguinte inesperadamente:
A estrutura seguinte, usando um campo int é perfeitamente legal:
struct MyStruct
{
public MyStruct ( int size )
{
this.Size = size; // <-- Legal assignment.
}
public int Size;
}
No entanto, a seguinte estrutura, usando uma propriedade automática não compila:
struct MyStruct
{
public MyStruct ( int size )
{
this.Size = size; // <-- Compile-Time Error!
}
public int Size{get; set;}
}
O erro é "O 'isto' objeto não pode ser usado antes de todos os seus campos são atribuídos a". Eu sei que este é um procedimento padrão para uma struct: o campo de apoio para qualquer propriedade deve ser atribuído directamente (e não através do conjunto acessor da propriedade) de dentro o construtor do struct.
A solução é usar um campo de apoio explícito:
struct MyStruct
{
public MyStruct(int size)
{
_size = size;
}
private int _size;
public int Size
{
get { return _size; }
set { _size = value; }
}
}
(Note que VB.NET não tem esse problema, porque em VB.NET todos os campos são automaticamente inicializados com 0 / null / false quando criada pela primeira vez.)
Esta parece ser uma limitação infeliz ao usar propriedades automáticas com estruturas em C #. Pensando conceitualmente, eu queria saber se isso não seria um lugar razoável para que haja uma exceção que permite o acessor conjunto de propriedades a ser chamado no construtor de um struct, pelo menos para uma propriedade automática?
Esta é uma questão menor, quase um caso limite, mas eu queria saber o que os outros pensavam sobre isso ...
Solução
De C # 6 em diante: isso não é mais um problema
Becore C # 6, você precisa chamar o construtor padrão para que isso funcione:
public MyStruct(int size) : this()
{
Size = size;
}
Um problema maior aqui é que você tem um struct mutável. Esta é não uma boa idéia. Gostaria de fazê-lo:
public int Size { get; private set; }
Não tecnicamente imutável, mas perto o suficiente.
Com as versões recentes do C #, você pode melhorar este:
public int Size { get; }
Isto pode agora única ser atribuído no construtor.
Outras dicas
Você pode corrigir isso, primeiro chamando o construtor padrão:
struct MyStruct
{
public MyStruct(int size) : this()
{
this.Size = size; // <-- now works
}
public int Size { get; set; }
}
Outra forma de contornar obscura para este problema é um visto na classe Tuple
temporária no Managed Extensibility Framework (via Krzysztof Kozmic ):
public struct TempTuple<TFirst, TSecond>
{
public TempTuple(TFirst first, TSecond second)
{
this = new TempTuple<TFirst, TSecond>(); // Kung fu!
this.First = first;
this.Second = second;
}
public TFirst First { get; private set; }
public TSecond Second { get; private set; }
(código-fonte completo do CodePlex: Tuple.cs )
Eu também notar que a documentação do CS0188 foi atualizado para adicionar:
Se você ver este erro ao tentar inicializar uma propriedade em um struct construtor, a solução é a mudança o parâmetro de construtor para especificar o campo de suporte em vez da própria propriedade. Auto-implementado propriedades devem ser evitadas em structs, porque eles não têm qualquer apoio campo e, portanto, não pode ser inicializado de alguma forma a partir da construtor.
Então, eu que isso significa que a orientação oficial é usar propriedades de estilo antigo em suas estruturas quando você correr para este problema, que é provavelmente menos obscuro (e mais readible) do que qualquer das outras duas alternativas exploradas de modo longe.