Dec 28, 2022

Um pequeno puzzle sobre o C# 10 - Solução (Parte III)

Read this post in English.

Lire cet post en Français.

Nos últimos posts, lancei o desafio para alterar a saída do programa a seguir para mostrar apenas linhas com números pares (leia as partes I e II se ainda não o fez).

Eu também disse que se você olhasse com cuidado no código, poderia encontrar algumas pistas sobre a direção da solução com base em trechos de código que não eram estritamente necessários naquela versão que basicamente contém 2 desses pedaços de código:
  1. O using System.Runtime.CompilerServices que não é necessário, pois não estamos fazendo referência a nenhum tipo declarado nesse namespace.
     
  2. o if  na linha 5 do método Foo() claramente não é necessário, pois o programa passa apenas strings não nulas/vazias para esse método.

Como sugeri, a solução que criei explora C# string interpolation handlers, um recurso introduzido no C# 10.

Munido com essas informações, uma maneira de atingir nosso objetivo consiste em simplesmente substituir o tipo de parâmetro msg por um custom string interpolation handler (InterpolatedHandlerTrick no código abaixo) da seguinte forma:

Se você executar o programa acima, verá que ele realmente funciona, ou seja, você deverá ver apenas as linhas com números pares na saída. Antes de mergulhar nos detalhes, vamos dar uma olhada no que é necessário para implementar custom interpolated string handlers:
  1. Declare um tipo e marque o mesmo com  InterpolatedStringHandlerAttribute  (linha #14)
  2. Implemente um construtor com pelo menos dois inteiros (literalLength e formattedCount) (nossa implementação declara alguns parâmetros extras para aproveitar alguns recursos mais avançados, que serão discutidos posteriormente)
  3. Implemente os métodos:
    1. void AppendLiteral(string msg)
    2. void AppendFormatted<T>(T value)
e pronto!
 
Isso é muito legal mas a pergunta interessante é: por que isso funciona?

Se você colar este código no sharplab.io e selecionar C# no menu suspenso Results,  você verá que o compiladorC# reescreveu seu programa (ou para manter a terminologia técnica desta transformação, o compilador Lowered o cópdigo), mais especificamente a chamada ao método Foo() para algo como:

O código real será um pouco diferente (basicamente ele converterá o loop for em um while e introduzirá algumas variáveis locais extras), mas decidi ignorar isso e mantê-lo o mais próximo possível do original para fins de comparação.

Observe que o compilador introduziu uma variável local do tipo do nosso handler  (linha 4) e fez algumas chamadas a métodos passando as várias partes da string interpolada; basicamente ele separou a string nos limites `{...}` e para cada parte, executou o método handler.AppendLiteral(part); (linha 7) seguido de handler.AppendFormatted(conteúdo da expressão {...}) (liha 8) .

Observe também que essas chamadas são agrupadas em um if (linha 5) controlado por uma variável que foi inicializada pelo construtor de nosso manipulador de string interpolado e voilà, temos todas as partes necessárias: o método que usa nosso manipulador especifica que um ou mais de seus parâmetros (neste caso apenas um) deve ser passado para o construtor do manipulador de string (através do InterpolatedStringHandlerArgumentAttribute) que ele usa para decidir se aquela string deve ser processada ou não, definindo um parâmetro ref bool (declarado como o último parâmetro do construtor) levando o código reduzido a pular as chamadas para os métodos AppendX()  e, portanto, a instância do manipulador de string passado para Foo() produz uma string vazia que falha a condição (if linha 5 do programa inicial) ignorando a chamado ao método Console.WriteLine()!

Legal, mas antes de finalizar, aqui estão algumas melhorias/considerações de desempenho/usabilidade que esse recurso traz:

  1. DefaultInterpolatedStringHandler é, como o próprio nome indica, usado sempre que uma string interpolada é passada para um método que usa strings como parâmetro, portanto, simplesmente recompilar seu código baseado em C# 9 com C# 10 deve trazer pelo menos algumas melhorias de desempenho/alocações.

  2. Uma vez que DefaultInterpolatedStringHandler é declarado como uma ref struct, sua instanciação não causará uma alocação no heap (em geral, todos os Interpolated String Handlers devem ser uma ref struct para evitar tais alocações).

  3. Uma vez que String.Format() não esa mais sendo usado, nenhum array é alocado.

  4. As alocações podem ser completamente evitadas quando o argumento especificado por meio de InterpolatedStringHandlerArgumentAttribute define  que a string resultante não será utilisada (muito útil em código do tipo logging/assertions)

  5. Esteja ciente que no caso do handler reportar que a string resultante não será utilizada (veja ponto anterior) side effects podem não ser observados; por exemplo, se nós modfificarmos o conteúdo da string interpolada (linha 10) de $"Test {i}" para $"Test {SomeMethod(i)}", o método SomeMethod() não será executado para valores ímpares  de o que não é óbvio apenas olhando a chamada ao método Foo() no código fonte to programa.

  6. Para minimizar ainda mais possíveis alocações no heap, considere implementar  ISpanFormattable em seus próprios tipos, se eles puderem ser usados como expressões em strings interpoladas (varios tipos da BLC o fazem).
Por fim, se você deseja saber mais sonbre esse recurso recomendo fortemente assistir este video e ler este tutorial da MS.

Divirta-se!

Adriano

Une petit casse-têtes C# 10 - La solution (Part III)

Leia este post em português

Read this post in English.

Dans les derniers messages, j'ai lancé un défi pour modifier la sortie du programme suivant pour afficher uniquement les lignes avec des nombres pairs (allez lire les parties I et II si vous ne l'avez pas déjà fait).

J'ai également dit que si vous regardez attentivement dans le code, vous pourrez trouver des indices sur la direction de la solution basée sur des morceaux de code qui n'étaient pas essentiels dans cette version du code qui contient essentiellement 2 de ces morceaux de code :

  1. l'utilisation de System.Runtime.CompilerServices qui n'est pas nécessaire car nous ne référençons aucun type déclaré dans cet espace de noms.

  2. le if sur la ligne 5 de la méthode Foo() qui, clairement n'est pas nécessaire car le programme ne transmet que des chaînes non nulles/vides à cette méthode.

Comme je l'ai laissé entendre, la solution que j'ai trouvée explore C# interpolated string handlers, une fonctionnalité introduite dans C#10.

Avec cet information la, nous pouvons atteindre notre objectif en simplement remplacent le type de paramètre msg par un interpolated string handler personnalisé (InterpolatedHandlerTrick dans le code ci-dessous) comme suit:

Si vous exécutez le programme ci-dessus, vous verrez qu'il fonctionne effectivement, c'est-à-dire que vous ne devriez voir que les lignes avec des nombres pairs dans la sortie. Avant de plonger dans les détails, examinons ce qui est nécessaire pour implémenter des interpolated string handler personnalisé :
  1. Déclarez un type et ajoutez-y l'InterpolatedStringHandlerAttribute (ligne #14)

  2. Implémenter un constructeur prenant au moins deux integers (literalLength et formattedCount) (notre implémentation déclare des paramètres supplémentaires pour profiter de certaines fonctionnalités  plus avancées, qui seront discutées plus tard)

  3. Mettre en œuvre les méthodes 
    1. void AppendLiteral(string msg)
    2. void AppendFormatted<T>(valeur T)
et c'est tout.

C'est tout belle et bien mais la question intéressante est : pourquoi ça marche ?

Si vous collez ce code dans
sharplab.io et sélectionnez C# dans le menu déroulant Résultats, vous verriez que le compilateur C# a réécrit votre programme (ou, pour s'en tenir à la terminologie technique de cette transformation, le compilateur a réduit le code), plus précisément l'appel à la méthode Foo() à quelque chose comme :

Le code réel sera un peu différent (essentiellement, il convertira la boucle for en un while et introduira des variables locales supplémentaires) mais j'ai décidé de l'ignorer et de le garder aussi proche que possible de l'original, ce qui simplifie le raisonnement.

Notez que le compilateur a introduit une variable locale typée comme notre interpolated string handler (ligne 4) et a effectué des appels de méthode dessus en passant les différentes parties de la chaîne interpolée ; fondamentalement, il l'a divise sur les limites `{...}` et pour chaque partie, il a appelé handler.AppendLiteral(part); (ligne 7) suivi pour handler.AppendFormatted(contents of {...}) (ligne 8) .

Notez également que ces appels sont enveloppés dans un if (ligne 5) contrôlé par une variable qui a été initialisée par le constructeur de notre handler et voilà, nous avons tous les éléments dont nous avons besoin pour implémenter le comportement souhaité ; la méthode prenant notre interpolated string handler spécifie qu'un ou plusieurs de ses paramètres (dans ce cas un seul) doivent être passés au constructeur de notre gestionnaire de chaîne (via InterpolatedStringHandlerArgumentAttribute) qu'il utilise pour décider si cette chaîne doit être traitée ou non par définir un paramètre ref bool (déclaré comme dernier paramètre du constructeur) conduisant le code réduit à ignorer les appels aux méthodes AppendX() et ainsi l'instance du gestionnaire de chaîne passé à Foo() produit une chaîne vide qui échoue la condition et saute Méthode Console.WriteLine() !

Cool, mais avant de vous quitter, voici quelques améliorations/considérations de performances que cette fonctionnalité apporte à la table :
  1. DefaultInterpolatedStringHandler est, comme son nom l'indique, utilisé chaque fois qu'une chaîne interpolée est transmise à une méthode qui prend une string comme paramètre, donc, simplement en recompilent votre code basé sur C# 9 avec C# 10 devrait vous apporter au moins quelques améliorations de performances/allocations.

  2. Étant donné que DefaultInterpolatedStringHandler est déclaré en tant que structure ref, son instanciation ne provoquera pas d'allocation de tas (en général, tous les gestionnaires de chaînes interpolées doivent être une structure ref pour éviter de telles allocations).

  3. Étant que String.Format() n'est plus utilisé, aucun array n'est alloué.

  4. Les allocations peuvent être complètement évitées lorsque l'argument spécifié via InterpolatedStringHandlerArgumentAttribute définit que la chaîne résultante ne sera pas utilisée (très utile dans la journalisation et le code d'assertion)

  5. Sachez que si le gestionnaire de chaîne interpolée signale que la chaîne résultante ne sera pas utilisée (voir le point ci-dessus), aucun effet secondaire lié à l'expression dans cette partie de la chaîne interpolée ne sera observé ; par exemple, si nous changeons la valeur interpolée dans le puzzle (ligne 10) de $"Test {i}" à $"Test {SomeMethod(i)}", SomeMethod() ne sera pas invoquée lorsque i est impair, ce qui peut ne pas être le cas être évident uniquement en inspectant l'appel à Foo()

  6. Pour minimiser les allocations, envisagez d'implémenter ISpanFormattable sur vos propres types s'ils peuvent être utilisés dans des chaînes interpolées (divers types de BLC implémentent cella).
Enfin, si vous souhaitez en savoir plus sur cette fonctionnalité, je vous recommande fortement de regarder cette vidéo et de lire ce tutoriel MS.

Have fun!

Adriano

A small C# 10 programming puzzle - Answer (Part III)

Leia este post em português

Lire cet post en Français.

In the last posts I've posed a challenge to change the output of the following program to show only lines with even numbers (go read parts I & II if you haven't done it yet).

I've also said that if you look carefully in the code you would be able to find some clues on the direction of the solution based on pieces of code that were not strictly  required in that version of the code which basically contains 2 such pieces of code:
  1. the using System.Runtime.CompilerServices which  is not required as we are not referencing any types declared in that namespace.
     
  2. the if on line 5 of Foo() method is clearly not necessary as the program only passes non null/empty strings to that method.

As I hinted, the solution I came up explores C# string interpolation handlers, a feature introduced in C#10.

With that information, one way to achieve our goal consists in simply replacing the type of msg parameter with a custom string interpolation handler (InterpolatedHandlerTrick in the code below) as follows:

If you run the above program, you'll see that it indeed works, i.e, you should see only the lines with even numbers in the output. Before diving into the details lets take a look on what is needed to implement custom interpolated string handlers:
  1. Declare a type and add the InterpolatedStringHandlerAttribute to it (line #14)
  2. Implement a constructor taking  at least two integers (literalLength and formattedCount) (our implementation declares some extra parameters to take advantage of some more advanced features, which will be discussed later)
  3. Implement the methods
    1. void AppendLiteral(string msg)
    2. void AppendFormatted<T>(T value)
and that is it.
 
That is all nice and cool but the interesting question is: why does it work?

If you paste that code in sharplab.io and select C# from the Results  drop down, you can see that the C# compiler re-wrote your program (or, to stick to the technical terminology for this transformation, the compiler lowered the code), more specifically the call to Foo() method to something like:

The actual code will be a little different (basically it will convert the for loop into a while and introduce some extra local variables) but I decided to ignore that and keep it as close to the original as possible making it simpler to reason about.

Notice that the compiler introduced a local variable typed as our interpolated string handler (line 4) and made some method calls on it passing the various parts of the interpolated string; basically it split it on `{...}` boundaries and for each part it called handler.AppendLiteral(part); (line 7) followed by handler.AppendFormatted(contents of {...}) (line 8) .

Notice also that those calls are wrapped in an if (line 5) controlled by a variable that has been initialized by the constructor of our interpolated string handler and voilà, we have all the required pieces: the method taking our string handler specifies that one or more of its parameters (in this case only one) must be passed to the constructor of our string handler (through the InterpolatedStringHandlerArgumentAttribute) which it uses to decide whether that string should be processed or not by setting a ref bool parameter (declared as the last constructor parameter) leading the lowered code to skip the calls to the AppendX() methods and so the instance of the string handler passed to Foo() produces and empty string which fails the condition and skips Console.WriteLine() method!

Cool, but before I leave you, here are some performance improvements/considerations this feature brings to the table:

  1. DefaultInterpolatedStringHandler is, at its name implies, used whenever an interpolated string is passed to a method that takes strings as a parameter, so, simply recompiling your C# 9 based code with C# 10 should bring you at least some performance/allocations improvements.

  2. Since DefaultInterpolatedStringHandler is declared as a ref struct its instantiation will not cause a heap allocation (in general all interpolated string handlers should be a ref struct to avoid such allocations).

  3. Since  String.Format() is not being used anymore no arrays are allocated.

  4. Allocations may be completely avoided when the argument specified through InterpolatedStringHandlerArgumentAttribute defines that the resulting string will not be used (very useful in logging and assertion code)

  5. Be aware that if the interpolated string handler reports that the resulting string will not be used (see point above), any related side effects of the expression in that part of the interpolated string will not be observed; for instance, if we change the interpolated value in the puzzle (line 10) from $"Test {i}" to $"Test {SomeMethod(i)}", SomeMethod() will not be invoked when i is odd which may not be obvious only inspecting the call to Foo()

  6. To minimize allocations even further, consider implementing ISpanFormattable on your own types if they may be used as expressions in interpolated strings (various types from BLC does implement that).
Finally, If you want to learn more about this feature I highly recommend to watch this video and read this MS tutorial.

Have fun!

Adriano

Nov 7, 2022

Une petit casse-têtes C# 10 - La solution - (Part II)

Leia este post em português

Read this post in English.

Dans mon dernier post j'ai lancé un défi pour modifier le contenu imprimé par le programme ci-dessous afin qu'il n'affiche que les lignes comprenant des nombres impairs.

La seule mise en garde est que j'ai limité ce qui pourrait être modifié comme suit:

  1. Vous ne pourrez pas modifier que le type du paramètre msg du méthode Foo.
  2. Vous ne pourrez pas modifier l’implémentation du loop for.
  3. Vous ne pourrez pas utiliser IL post-processing (mais ça serait une exercice intéressant 😃)
  4. Toutes les autres modifications dans le de code sont valides.

J'ai également dit que si vous regardiez attentivement dans le code, vous seriez en mesure de trouver des indices sur la direction de la solution basée sur des morceaux de code qui n'étaient pas essentiels. J'y reviendrai plus tard, mais permettez-moi d'abord de vous présenter rapidement une fonctionnalité C# qui existe depuis C# 6 et qui sera utilisée dans ma solution finale : Interpolated strings.

Ce sont des littéraux de chaînes préfixés par le caractère $ et contenant des expressions C# entourées d'accolades ({ ... }) pour lesquelles le compilateur C# émettra du code pour remplacer ces expressions par leurs valeurs respectives ; l'idée principale est de simplifier le code de formatage des chaînes. Par exemple, dans le programme ci-dessous:

Le compilateur C# remplacera {i} par la valeur de la variable i et {args[i]} par la valeur du élément à l'index i du array args.

Lorsque la prise en charge des chaînes interpolées a été introduite pour la première fois, le compilateur émettait un code similaire à:

Ou, en d'autres termes, il remplacerait la interpolated string par un ou plusieurs appels à String.Format() (comme indiqué à la ligne 10 ci-dessus). Il n'y a rien de mal à cela, après tout, c'est presque exactement le code qu'un développeur écrirait en l'absence de prise en charge de cet fonctionnalité, mais avec la poussée des performances ces dernières années, les développeurs .NET ont réalisé qu'il y avait une marge d'amélioration dans ce domaine; en effet, si vous examinez le code ci-dessus, il existe quelques codes non idéaux, en termes de mémoire:

  1. à ligne #5, il y a une allocation de un array.
  2. il y a 3 opérations de boxing (lignes #6, 7 & 8)
  3. La méthode String.Format() method finira probablement par appeler Int32.ToString() qui allouera une nouvelle string (au moins sur certaines versions de .NET).

Dans les petits programmes (ou même moyens?) cela ne ferait aucune différence, mais sur les applications plus grandes, ou les applications qu'utilisant fortement la manipulation de strings, cela s'est avéré être une source potentielle de problèmes de performances, donc en C # 10, le concept de string interpolation handlers, le sujet du prochain post, a été introduit pour aborder certains d'entre eux.

Have fun!

Adriano

A small C# 10 programming puzzle - Answer (Part II)

Leia este post em português

Lire cet post en Français.

In the last post I've posed a challenge to change the output of the following program to show only lines with even numbers.

The only caveat is that I've restricted what could be changed as follow:

  1. You could not  change Foo's  implementation in any way other than changing its msg parameter type.
  2. You could not change the implementation of the for loop in any ways.
  3. You could not on IL post-processing in any way (but it would be a nice exercise 😃)
  4. Any other changes to the source are acceptable

I've also said that if you look carefully in the code you would be able to find some clues on the direction of the solution (at least the one I was thinking about) based on pieces of code that were non essential. I'll come back to those later but first let me quickly introduce a C# feature that has been around since C# 6 that will be used in my final solution: Interpolated strings.

Interpolated strings are strings literals prefixed with $ character and containing C# expressions wrapped in braces ({ ... }) for which C# compiler will emit code to replace such expressions with their respective values; the main idea is to simplify string formatting code. For example, in the program below:

C# compiler will replace {i} with the value of variable i and {args[i]} with the value of the element at index i of the args array.

When the support for interpolated strings was first introduced the compiler would emit code similar to:

Or, in other words, it would replace the interpolated string with one or more calls to String.Format()(as show in line #10 above). There's nothing wrong with that, after all, that is almost exactly the code a developer would write in the absence of interpolated string support, but with the push for performance in the recent years, the .NET developers realized that there was some margin for improvements in that area; indeed, if you look into the code above, there are a couple of non-ideal code, memory wise:

  1. In line #5, there's an array allocation.
  2. There are 3 boxing operations (lines #6, 7 & 8)
  3. String.Format() method will likely end up calling Int32.ToString() which will allocate a new string (at least on some versions of .NET).
In small (or even medium ?) programs that would barely make any difference, but on bigger applications making heavily use of string manipulation that proved to be a potential source of performance issues so in C# 10 the concept of string interpolation handlers, the subject of the next post, was introduced to address some of those.

Have fun!

Adriano

Um pequeno puzzle sobre o C# 10 - Solução (Parte II)

Read this post in English

Lire cet post en Français.

Em meu último post apresentei um desafio para modificar a saída do programa abaixo de forma que o mesmo apresentasse apenas as linhas contendo números pares.

As únicas restrições  eram que você:

  1. Não poderia modificar a implementação do método Foo, a não ser, o tipo do parâmetro msg.
  2. Não poderia modificar a implementação do laço for.
  3. Não poderia utilizar IL post-processing
  4. Quaisquer outras modificações seriam aceitas.

Eu também mencionei que ao observar atentamente o programa serial possível encontrar pistas da solução que eu estava pensando na forma de código que não era essencial para que o programa apresentado compilasse/funcionasse; obviamente eu vou discutir estes pontos, mas antes, gostaria de apresentar a funcionalidade base (introduzida no C# 6) a qual será usada na minha solução: Interpolated strings.

Interpolated strings tem como objetivo simplificar o código de formatação de strings sendo formadas por strings literais prefixadas com o caractere $ e que contem expressões C# envolvidas em chaves ({ ... }) as quais são substituídas pelos seus respectivos valores pelo compilador C#. Por exemplo, no programa abaixo:

O compilador C# substituirá {i} pelo valor da variável i e {args[i]} pelo valor do elemento armazenado no índice i do array args.

Até a versão 9 do C#, o código acima seria convertido pelo compilador para algo como:

Ou, em outras palavras, o compilador substituiria a interpolated string com uma ou mais chamadas ao método String.Format()(como observado na linha #10 acima). Ainda que este  código não seja essencialmente incorreto (afinal de contas este seria mais ou menos o código usado na ausência desta funcionalidade), com a crescente busca na melhoria de performance no últimos anos, os desenvolvedores .NET notaram que existia margem para melhorias nesta área; e realmente, se observarmos atentamente notaremos claramente alguns padrões, não ideais, com relação à utilização de memória:

  1. Na linha #5, há uma alocação de um array.
  2. Existem 3 operações de boxing (lines #6, 7 & 8)(ou seja, alocações no heap)
  3. O método String.Format() muito provavelmente executará o método Int32.ToString() o qual acabará introduzindo alocações de strings no heap.

Note que, em programas pequenos (ou talvez mesmo em médios ?) estas alocações  seriam praticamente desprezíveis; contudo em aplicações maiores ou aplicações que fazem uso constante de tais operações estas alocações podem introduzir problemas de performance devido ao mecanismo de Garbage Collection (ou GC) necessitar executar constantemente.

Com o objetivo de melhorar a performance de tais aplicativos, C# 10 introduziu o conceito que abordaremos no próximo post e que será usado na solução do problema proposto: string interpolation handlers.

Have fun!

Adriano

Sep 30, 2022

Um pequeno puzzle sobre o C# 10

Lire cet post en Français.

Read this post in English.

Você desenvolve em C# no seu dia a dia? Tem acompanhado os últimos avanços desta linguagem, mais especificamente na versão 10? Gosta de desafios de programação ? 

Caso a resposta a uma ou mais das perguntas acima seja sim, você conseguiria, mudando apenas o tipo do parâmetro msg do método Foo(), fazer com que a saída do programa contenha apenas as linhas com números pares?

ou seja, fazer com que a saída do programa seja:

ao invés de:

As regras são:

  1. A única modificação válida no método Foo() é alterar o tipo do parâmetro msg.
  2. Você não pode modificar o corpo do programa, principalmente o laço for.
  3. Você não pode utilizar nenhuma técnica de IL post-processing.
  4. Qualquer outra alteração no código fonte é válida.

Seguem algumas considerações:

  1. A solução para o problema proposto envolve um recurso que foi aprimorado na versão 10 da linguagem.
  2. Observe bem o código acima; procure por elementos não essenciais; estes elementos contém pistas que podem ajudar.

Nos próximos posts eu vou apresentar e discutir a minha solução.

Divirta-se.

Adriano

Une petit casse-têtes C# 10

Leia este post em português.

Read this post in English.

Êtes-tu une développeur/développeuse utilisant C# au quotidien? Suives-tu les derniers C# 10 développements? Êtes-tu intéressé par les casse-têtes? 

Si tu as répondu oui a des questions ci-dessus, étant donné le programme suivant, pouvez-vous, en changeant uniquement le type du paramètre msg dans la méthode Foo(), modifier la sortie du programme pour n'imprimer que les lignes contenant des nombres pairs?

c'est-à-dire, changer la sortie du programme par :

au lieu de:

Bref, les réglés sont:

  1. C'est interdit de changer l’implémentation de la méthode  Foo(), sauf changer le type du paramétrer msg.
  2. C'est interdit de changer le loop for, n'importe en quelle manière.
  3. C'est interdit de utiliser IL post-processing
  4. Tous les autres types de changement das le source code sont valides.

Suivant tu peux trouver quelque conseils dans le cas tu ne savais pas déjà le réponse:

  1. La solution (au moins laquelle que je suis intéresse) dépend dans une fonctionnalité qui s'était amélioré sur la version 10 de la langage.
  2. Regarde le programme attentivement en cherchent pour les pièces non essentielles;  elles contiennent quelques indices importantes pour trouver la solution.

Dans le prochain post je montrerai et discuterai ma solution.

Amuse-toi.

Adriano

A small C# 10 programming puzzle.

Leia este post em português

Lire cet post en Français.

Are you a developer using C# in a day to day basis? Are you following the latest development, mainly in the C# 10 version? Are you interested in puzzles? 

If so, given the following program, can you, changing only the  type of the parameter msg in Foo() method,  change the output of the program to print only the lines containing even numbers?

I.e printing:

instead of:

Summarizing, the rules are:

  1. You cannot change Foo's  implementation in any way other than changing its msg parameter type.
  2. You cannot change the implementation of the for loop in any ways.
  3. You cannot rely on IL post-processing in any way (but it would be a nice exercise 😃)
  4. Any other changes to the source are acceptable

Some tips, in case you don't know the answer yet:

  1. The solution for the proposed challenge (at least the one I am interested) depends on a C# feature that has been improved in version 10
  2. Inspect the source with attention and look for suspicious code, they do contain some clues on how to achieve your goal.

In the next posts I'll show, and discuss, my solution to that. I am curious to see what other solutions exists.

Have fun!

Adriano