No .NET, um tipo de valor (C # struct
) não pode ter um construtor sem parâmetros. De acordo com este post, isso é obrigatório pela especificação da CLI. O que acontece é que, para cada tipo de valor, é criado um construtor padrão (pelo compilador?) Que inicializa todos os membros como zero (ou null
).
Por que não é permitido definir um construtor padrão?
Um uso trivial é para números racionais:
public struct Rational {
private long numerator;
private long denominator;
public Rational(long num, long denom)
{ /* Todo: Find GCD etc. */ }
public Rational(long num)
{
numerator = num;
denominator = 1;
}
public Rational() // This is not allowed
{
numerator = 0;
denominator = 1;
}
}
Usando a versão atual do C #, um Rational padrão é o 0/0
que não é tão legal.
PS : Os parâmetros padrão ajudarão a resolver isso no C # 4.0 ou o construtor padrão definido pelo CLR será chamado?
Jon Skeet respondeu:
Para usar seu exemplo, o que você gostaria que acontecesse quando alguém fizesse:
Rational[] fractions = new Rational[1000];
Ele deve executar seu construtor 1000 vezes?
Claro que deveria, foi por isso que escrevi o construtor padrão em primeiro lugar. O CLR deve usar o construtor de zeragem padrão quando nenhum construtor padrão explícito é definido; Dessa forma, você paga apenas pelo que usa. Então, se eu quiser um contêiner de 1000 Rational
s não padrão (e quiser otimizar as 1000 construções), usarei List<Rational>
uma matriz em vez de uma matriz.
Este motivo, em minha opinião, não é forte o suficiente para impedir a definição de um construtor padrão.
Rational()
chama o ctor sem parâmetros em vez deRational(long num=0, long denom=1)
.new Rational()
chamará o construtor se ele existir, no entanto, se não existir,new Rational()
será equivalente adefault(Rational)
. De qualquer forma, é recomendável usar a sintaxedefault(Rational)
quando desejar o "valor zero" da sua estrutura (que é um número "ruim" com o design propostoRational
). O valor padrão para um tipo de valorT
é sempredefault(T)
. Portantonew Rational[1000]
, nunca invocará struct constructors.denominator - 1
no interior da estrutura, de modo que o valor padrão se torna 0/1Then if I want a container of 1000 non-default Rationals (and want to optimize away the 1000 constructions) I will use a List<Rational> rather than an array.
Por que você esperaria que uma matriz chamasse um construtor diferente para uma lista para uma estrutura?Respostas:
Nota: a resposta abaixo foi escrita muito tempo antes do C # 6, que planeja introduzir a capacidade de declarar construtores sem parâmetros em estruturas - mas eles ainda não serão chamados em todas as situações (por exemplo, para criação de matriz)(no final esse recurso não foi adicionado ao C # 6 ).Edição: Eu editei a resposta abaixo devido à visão de Grauenwolf sobre o CLR.
O CLR permite que os tipos de valor tenham construtores sem parâmetros, mas o C # não. Eu acredito que isso ocorre porque introduziria uma expectativa de que o construtor seria chamado quando não o faria. Por exemplo, considere isso:
O CLR é capaz de fazer isso com muita eficiência apenas alocando a memória apropriada e zerando tudo. Se tivesse que executar o construtor MyStruct 1000 vezes, isso seria muito menos eficiente. (Na verdade, isso não acontece - se você faz tem um construtor sem parâmetros, ele não é executado quando você criar uma matriz, ou quando você tem uma variável de instância não inicializada.)
A regra básica em C # é "o valor padrão para qualquer tipo não pode confiar em nenhuma inicialização". Agora eles poderiam ter permitido que construtores sem parâmetros fossem definidos, mas não exigiam que o construtor fosse executado em todos os casos - mas isso levaria a mais confusão. (Ou pelo menos, então acredito que o argumento continua.)
EDIT: Para usar seu exemplo, o que você gostaria que acontecesse quando alguém fizesse:
Ele deve executar seu construtor 1000 vezes?
EDIT: (Respondendo um pouco mais à pergunta) O construtor sem parâmetros não é criado pelo compilador. Tipos de valor não tem que ter construtores, tanto quanto o CLR está em causa - embora ele sair ele pode se você escrevê-lo em IL. Quando você escreve "
new Guid()
" em C # que emite IL diferente do que você obtém se chamar um construtor normal. Veja esta pergunta SO para um pouco mais sobre esse aspecto.Eu suspeito que não há nenhum tipo de valor na estrutura com construtores sem parâmetros. Sem dúvida, o NDepend poderia me dizer se eu pedisse o suficiente ... O fato de o C # proibir é uma dica grande o suficiente para eu pensar que é provavelmente uma má idéia.
fonte
Rational[] fractions = new Rational[1000];
também desperdiçar uma carga de trabalho, seRational
é uma classe em vez de um struct? Se sim, por que as classes têm um controlador padrão?Rational
for uma classe, você terá uma matriz de 1000 referências nulas.Uma estrutura é um tipo de valor e um tipo de valor deve ter um valor padrão assim que for declarado.
Se você declarar dois campos como acima, sem instanciar, interrompa o depurador,
m
será nulo, masm2
não será. Dado isso, um construtor sem parâmetros não faria sentido; na verdade, todo construtor de uma estrutura faz atribuir valores, a coisa em si já existe apenas ao declará-la. De fato, o m2 poderia muito bem ser usado no exemplo acima e ter seus métodos chamados, se houver, e seus campos e propriedades manipulados!fonte
new
It, e depois tentar usar seus membrosEmbora o CLR permita, o C # não permite que as estruturas tenham um construtor padrão sem parâmetros. O motivo é que, para um tipo de valor, os compiladores, por padrão, não geram um construtor padrão, nem geram uma chamada para o construtor padrão. Portanto, mesmo que você tenha definido um construtor padrão, ele não será chamado e isso apenas o confundirá.
Para evitar esses problemas, o compilador C # não permite a definição de um construtor padrão pelo usuário. E como ele não gera um construtor padrão, você não pode inicializar campos ao defini-los.
Ou o grande motivo é que uma estrutura é um tipo de valor e os tipos de valor são inicializados por um valor padrão e o construtor é usado para a inicialização.
Você não precisa instanciar sua estrutura com a
new
palavra - chave. Em vez disso, funciona como um int; você pode acessá-lo diretamente.As estruturas não podem conter construtores explícitos sem parâmetros. Os membros do Struct são inicializados automaticamente com seus valores padrão.
Um construtor padrão (sem parâmetros) para uma estrutura pode definir valores diferentes do estado com zero, o que seria um comportamento inesperado. Portanto, o tempo de execução do .NET proíbe construtores padrão para uma estrutura.
fonte
MyStruct s;
não chamar o construtor padrão que você forneceu.Você pode criar uma propriedade estática que inicialize e retorne um número "racional" padrão:
E use-o como:
fonte
Rational.Zero
pode ser um pouco menos confuso.Explicação mais curta:
Em C ++, struct e classe eram apenas dois lados da mesma moeda. A única diferença real é que um era público por padrão e o outro era privado.
No .NET , há uma diferença muito maior entre uma estrutura e uma classe. O principal é que struct fornece semântica de tipo de valor, enquanto classe fornece semântica de tipo de referência. Quando você começa a pensar nas implicações dessa mudança, outras também começam a fazer mais sentido, incluindo o comportamento do construtor que você descreve.
fonte
new
realmente deve ser escrito para chamar um construtor. Em C ++, os construtores são chamados de formas ocultas, na declaração ou instanciação de matrizes. No C #, tudo é um ponteiro; portanto, comece com null, é uma estrutura e deve começar com algo, mas quando você não pode escrevernew
... (como array init), isso quebraria uma regra forte de C #.Eu não vi o equivalente à solução tardia que vou dar, então aqui está.
use deslocamentos para mover valores do padrão 0 para qualquer valor que desejar. aqui as propriedades devem ser usadas em vez de acessar diretamente os campos. (talvez com o possível recurso do c # 7, você defina melhor os campos com escopo de propriedade para que eles permaneçam protegidos contra o acesso direto no código.)
Esta solução funciona para estruturas simples com apenas tipos de valor (sem tipo de referência ou estrutura anulável).
Isso é diferente de resposta, essa abordagem não é uma caixa especial, mas sua utilização de deslocamento que funcionará para todos os intervalos.
exemplo com enumerações como campo.
Como eu disse, esse truque pode não funcionar em todos os casos, mesmo que struct tenha apenas campos de valor, somente você sabe que, se funcionar no seu caso ou não. apenas examine. mas você entendeu a ideia geral.
fonte
Apenas caso especial. Se você vir um numerador de 0 e um denominador de 0, finja que ele tem os valores que você realmente deseja.
fonte
Nullable<T>
(por exemploint?
).new Rational(x,y)
onde xey são 0?O que eu uso é o operador coalescente nulo (??) combinado com um campo de apoio como este:
Espero que isto ajude ;)
Nota: a atribuição de coalescência nula é atualmente uma proposta de recurso para C # 8.0.
fonte
Você não pode definir um construtor padrão porque está usando C #.
As estruturas podem ter construtores padrão no .NET, embora eu não conheça nenhuma linguagem específica que a suporte.
fonte
Aqui está minha solução para o dilema de construtor sem padrão. Sei que esta é uma solução tardia, mas acho que vale a pena notar que é uma solução.
ignorando o fato de eu ter uma estrutura estática chamada null, (Observação: isso é apenas para todos os quadrante positivo), usando get; set; em C #, você pode tentar / capturar / finalmente, para lidar com os erros em que um determinado tipo de dados não é inicializado pelo construtor padrão Point2D (). Eu acho que isso é evasivo como uma solução para algumas pessoas nesta resposta. Isso é principalmente porque eu estou adicionando o meu. O uso da funcionalidade getter e setter em C # permitirá ignorar esse construtor padrão sem sentido e tentar o que você não inicializou. Para mim, isso funciona bem; para outra pessoa, você pode querer adicionar algumas declarações if. Portanto, no caso em que você deseja uma configuração de Numerador / Denominador, esse código pode ajudar. Gostaria apenas de reiterar que esta solução não parece boa, provavelmente funciona ainda pior do ponto de vista de eficiência, mas, para alguém que vem de uma versão mais antiga do C #, o uso de tipos de dados de matriz oferece essa funcionalidade. Se você quer apenas algo que funcione, tente o seguinte:
fonte
fonte