Mar 1, 2025

Structs em C# - diversão garantida - Parte 8/9: Struct usada como valores padrão para argumentos

Read this post in English.

Lire cet article en français.

Em um post anterior examinamos o uso de parâmetros com valores default em construtores de structs; desta vez, vamos dar uma olhada rápida no que acontece quando temos parâmetros do tipo struct com valores default. Então, sem mais delongas, vamos começar com a seguinte struct1:

struct S
{
    public int v = 42;
    public S(string s) { v = 13; }
}

e o seguinte uso:

M();

void M(S s = new S()) => Console.WriteLine(s.v);

Qual saída você espera obter?

Se você tem acompanhado esta série de posts provavelmente notou que esta é uma pergunta capciosa e que nem 42 nem 13 são respostas corretas uma vez que para que tais valores pudessem ser impressos isso exigiria que o inicializador de campo e/ou o construtor fossem executados, mas a expressão new S() usada como valor default em parâmetros não implica uma invocação de construtor (afinal, não há um construtor sem parâmetros e, caso você esteja tentado a adicionar um, o compilador emitirá um erro, pois, na presença de tal construtor, a expressão em questão não representa mais uma constante de tempo de compilação, portanto, não pode ser usada como um valor default).

Na realidade, quando executado o trecho de código exibirá 0 pois o compilador simplesmente inicializa a memória usada para armazenar a instância da struct com zeros (0). Por outro lado, se você invocar o método M() como a seguir2:

M(new S("Foo"));

o compilador emitirá código para executar o construtor e o inicializador de campo e 13 será impresso, mas isso não tem mais nada a ver com o valor de parâmetro padrão.

E com isso exploramos o último comportamento não tão intuitivo de estruturas que pretendíamos abordar; o próximo post será a conclusão desta série.

Como sempre, todos os comentários são bem-vindos.

Divirta-se!


  1. Observe que o suporte para inicializadores de campo em structs foi introduzido no C# 10.

  2. O conteúdo da string não é importante neste contexto. Tudo o que importa é que um construtor seja invocado.

Les structures en C# sont amusantes - Partie 8/9: Structure utilisée comme valeurs d'argument défaut

Leia este post em Português.

Read this post in English.

Dans un article précédent, nous avons examiné l'utilisation de valeurs de paramètres par défaut dans les constructeurs de structures ; cette fois, nous allons examiner rapidement ce qui se passe lorsque nous avons des paramètres de structure avec des valeurs par défaut. Alors, sans plus tarder, commençons par la structure suivante[^1] :

struct S
{
    public int v = 42;
    public S(string s) { v = 13; }
}

et l'utilisation suivante :

M();

void M(S s = new S()) => Console.WriteLine(s.v);

Quelle sortie attendez-vous à obtenir ?

Si vous avez suivi cette série d'articles, vous savez probablement qu'il s'agit d'une question piège et que ni 42 ni 13 ne sont les bonnes réponses, car cela nécessiterait que l' initialiseur de champ et/ou le constructeur soient exécutés, mais l'expression new S() utilisée comme valeur de paramètre par défaut n'implique pas un appel de constructeur (après tout, il n'y a pas de constructeur sans paramètre et si vous êtes tenté d'en ajouter un, le compilateur émettra une erreur car en présence d'un tel constructeur, l'expression en question ne représente plus une constante de compilation, d'où elle ne peut pas être utilisée comme valeur de paramètre par défaut).

Lorsqu'il est exécuté, cet extrait de code affichera 0 car le compilateur initialise simplement la mémoire complète utilisée pour stocker l'instance de structure avec des zéros (0). Par contre, si vous appelez la méthode M() comme suit[^2]

M(new S("Foo"));

dans cette cas le compilateur émettra du code pour exécuter le constructeur et l'initialiseur de champ et 13 sera affiché, mais cela n'a plus rien à voir avec la valeur de paramètre par défaut.

Et avec cela, nous avons exploré le dernier comportement de structure pas si intuitif que nous avions l'intention de couvrir ; le prochain article sera la conclusion de cette série.

Comme toujours, tous les commentaires sont les bienvenus.

Amusez-vous bien !


[^1] : Notez que la prise en charge des initialiseurs de champs dans les structures a été introduite dans C# 10.

[^2] : Le contenu de la chaîne n'est pas important dans ce contexte. Tout ce qui compte, c'est qu'un constructeur est appelé.

Structs in C# are fun - Part 8/9: Struct used as default argument values

Leia este post em Português.

Lire cet post en français.

In a previous post we looked at having default parameter values in struct constructors; this time we are going to have a quick look on what happens when we have struct parameters with default values. So, without further ado lets start with the following struct1:

struct S
{ 
    public int v = 42;
    public S(string s) { v = 13; }
}

and the following usage:

M();

void M(S s = new S()) => Console.WriteLine(s.v);

What you expect the output to be?

If you have been following this post series you probably know that this is a trick question and that neither 42 nor 13 are correct answers since that would require that either the field initializer and/or the constructor to be executed but the expression new S() used as the default parameter value does not implies a constructor invocation (after all there's no such parameterless ctor and if you feel tempted to add one the compiler will happily emit an error since in the presence of such ctor the expression in question does not represent a compile time constant anymore whence it cannot be used as a default parameter value).

When executed that code snippet will output 0 because the compiler simply initializes the full memory used to store the struct instance with zeros (0). By the other hand if you invoke method M() as follows2

M(new S("Foo"));

then the compiler will emit code to execute the constructor and the field initializer and 13 will be printed out, but this has nothing to do with the default parameter value anymore.

And with that we have explored the last not so intuitive struct behavior we intended to cover; next post will be the conclusion of this series.

As always, all feedback is welcome.

Have fun!


  1. Note that support for field initializers in structs was introduced in C# 10.

  2. The content of the string is not important in this context. All that matters is that a constructor is invoked.