Showing posts with label dotnet. Show all posts
Showing posts with label dotnet. Show all posts

Jan 29, 2025

Structs em C# - diversão garantida - Parte 7/9: A funcionalidade *required members* do C# 11 não vai salvar seu emprego

Lire cet post en français.

Read this post in English

A história deste post é um pouco embaraçosa.

Durante o processo de definição dos tópicos que eu cobriria, aprendi sobre uma funcionalidade do C# 11 chamada required members que, ingenuamente, pensei poderia ser usada para sinalizar esses cenários, então planejei adicionar um post mostrando como alcançar isso; no entanto, durante a investigação/redação1 do conteúdo percebi que esse não era um dos objetivos dessa funcionalidade e que havia vários casos extremos em que nenhum aviso seria emitido, mesmo que nenhum construtor fosse invocado2.

A ideia principal era marcar todos os membros (campos/propriedades) que seriam inicializados nos construtores como required e adicionar o atributo SetsRequiredMembers a esses construtores, de forma que, nos casos em que nenhum construtor (decorado com SetRequiredMembersAttribute) fosse invocado, o compilador emitiria um aviso/erro devido à não inicialização desses membros.

Essa técnica funciona relativamente bem se quisermos capturar um cenário muito problemático: a instanciação de tipos de valor com um construtor em que todos os seus parâmetros possuim valores opcionais3. Para tornar a discussão mais concreta, vamos pegar nosso último exemplo do post anterior e modificá-lo conforme descrito acima:

Print(new S2());
Print(new S2(13));

void Print(S2 s) => System.Console.WriteLine(s.v);

struct S2
{ 
    public required int v;
    
    [System.Diagnostics.CodeAnalysis.SetsRequiredMembers]
    public S2(int i = 42)  => v = i;
}

Com essa mudança em vez de obter silenciosamente uma instância de S2 inicializada com zero (em oposição ao esperado 42 se o construtor fosse invocado), o compiladore emite o seguinte erro:

error CS9035: O membro obrigatório 'S2.v' deve ser definido no inicializador de objeto ou no construtor de atributo.

Não é perfeito, já que a mensagem provavelmente será muito confusa se alguém estiver (incorretamente, mas compreensivelmente) esperando que o construtor de S2 seja invocado, mas há outras limitações que tornam essa abordagem ainda menos viável:

  • Incapacidade de detectar construtores não invocados (pelo menos) em expressões default e instanciações de arrays.
  • Mesmo em cenários em que isso funcionaria, é impossível garantir que um construtor será invocado (por exemplo, se mudarmos o código na linha #1 para new S2() { v = 5 }, nenhum construtor será invocado, mas nenhum aviso/erro será emitido)

Uma alternativa mais eficaz (se você implantar sua aplicação como gerenciada em vez de compilá-la AOT) para detectar tais cenários é garantir explicitamente que instâncias de struct foram inicializadas4 (seja por um construtor ou por outros meios) antes de acessar seus membros; como o código está gerado em tempo de execução pelo JIT, é possível implementar isso de forma que os usuários possam controlar se tal verificação deve ser aplicada ou com muito pouco (ou nenhum) impacto em desempenho quando a verificação está desativada, como demonstrado abaixo.

using System.Runtime.CompilerServices;

class Driver
{
    static void Main()
    {

        for(int i = 0; i < 100_000; i++)
        {
            var foo = new Foo(); // Nenhum construtor invocado... nenhum aviso :(
            Thread.Sleep(100);
            foo.Use();
        }
    }

}

// A estrutura pode ser declarada em um assembly diferente também. 
struct Foo
{
    // é importante que o campo seja marcado como `readonly`
    private static readonly bool _shouldValidate = Environment.GetEnvironmentVariable("VALIDATE_FOO") == "true";

    private int _i;
    private bool _isInitialized;

    public Foo(int v = 1) { _i = v; _isInitialized = true; }

    public void Use()
    {
        Verify();
        System.Console.WriteLine(_i);
    }

    [MethodImpl(MethodImplOptions.AggressiveInlining)] // É importante pedir que o JIT inline este método para que a otimização seja aplicada.
    private readonly void Verify()
    {
        if (_shouldValidate)
        {
            if (!_isInitialized)
            {
                throw new Exception("O construtor de Foo não foi invocado; isso pode ser devido a uma declaração de array ou ...");
            }
        }
    }
}

O código usa o campo _shouldValidate para controlar se a verificação de inicialização4 deve ser aplicada ou não (neste caso, mais especificamente, se o construtor foi executado). Observe que o mesmo é declarado como static readonly; isso é muito importante pois assim o JIT sabe que, uma vez inicializada uma dada instância de struct, o valor do campo nunca mudará, podendo tratar _shouldValidate como uma constante não gerando código para verificá-lo no if na linha #XX; além disso, caso esta constante seja avaliada como false, o JIT pode remover o if (incluindo seu corpo) completamente (daí o custo quase zero mencionado anteriormente).

Você pode ver essa mágica do JIT em ação abrindo um terminal, criando uma aplicação de console com o código acima e executando:

DOTNET_JitDisasm=Use dotnet run -c Release

o qual:

  1. em sistemas operacionais do tipo unix, define a variável de ambiente DOTNET_JitDisasm como Use, o que instrui o JIT a fazer o dump do código assembly gerado para o método de mesmo nome.
  2. compila a aplicação em modo release (-c Release), o que é um requisito para que a otimização seja aplicada.
  3. executa a aplicação.

Ao executar DOTNET_JitDisasm=Use dotnet run -c Release, você deve ver zeros (0) e algum código assembly sendo impresso no terminal várias vezes; após algumas iterações, você deve ser capaz de identificar código assembly semelhante ao abaixo (certifique-se de verificar o que mesmo contém Tier1 em vez de Tier0):

; Assembly listing for method Foo:Use():this (Tier1)
; Emitting BLENDED_CODE for X64 with AVX - Unix
; Tier1 code
; optimized code
; rsp based frame
; fully interruptible
; No PGO data
; 1 inlinees with PGO data; 0 single block inlinees; 0 inlinees without PGO data

G_M000_IG01:                ;; offset=0x0000
 
G_M000_IG02:                ;; offset=0x0000
       mov      edi, dword ptr [rdi]
 
G_M000_IG03:                ;; offset=0x0002
       tail.jmp [System.Console:WriteLine(int)]
 
; Total bytes of code 8

que basicamente chama System.Console.WriteLine(_i) e retorna, sem nenhum traço da invocação do método Verify().

Você também pode brincar com este exemplo executando-o como:

DOTNET_JitDisasm=Use VALIDATE_FOO=true dotnet run -c Release

nesse caso, ele lançará uma exceção (provando que o uso de instâncias de struct não inicializadas é detectado)

ou

DOTNET_JitDisasm=Use dotnet run -c Debug

não importando nesse caso quanto tempo a aplicação seja executada, o código assembly gerado para Use() sempre chamará Verify() (ou seja, a otimização não foi aplicada porque a aplicação foi compilada em modo debug)

Com essa abordagem, pode-se ter certeza de que nenhum código está usando instâncias não inicializadas simplesmente executando o código com a variável de ambiente definida como true e observando exceções.

Como sempre, todos os feedbacks são bem-vindos.

Divirta-se!


  1. aqui você pode encontrar alguns códigos de teste que usei enquanto explorava este tópico.

  2. Depois de perceber isso, mudei o título do post :).

  3. Este caso específico é problemático devido à expectativa de que o comportamento corresponderia ao comportamento para classes.

  4. Nota de esclarecimento: Do ponto de vista da runtime, structs são garantidas de serem inicializados (zerando toda a struct) antes de serem usadas. Inicialização no contexto desta série significa que todos os campos/propriedades da struct foram atribuídos valores significativos, deixando a instância em um estado consistente.

Jan 2, 2025

Structs in C# are fun - Part 7/9: Required feature from C# 11 will not save your a** job.

Leia este post em Português.

Lire cet post en français.

The history of the current post is a little bit embarrassing.

During the process of defining the topics I'd cover I've learned about a C# 11 feature called required members which I naively though could be used to flag these scenarios so I planned to add a post showing how to achieve that; however during investigation/drafting1 the content, I've realized that that was not one of the goals of this feature and that there were multiple corner cases in which no warning would be emitted even though no constructor would be invoked2.

The main idea would be to to mark all members (field/properties) that would be initialized in the constructors as required and add the SetsRequiredMembers attribute to these constructors in such a way that in cases where no constructors (decorated with SetRequiredMembersAttribute) were to be invoked the compiler would emit a warning/error due to the non-initialization of such members.

This technique works relatively well if one wants to catch a very problematic scenario: instantiation of value types with a constructor in which all of its parameters are optional3. To make the discussion more concrete, lets take our last example from the previous post and modify it as described above:

Print(new S2());
Print(new S2(13));

void Print(S2 s) => System.Console.WriteLine(s.v);

struct S2
{ 
    public required int v;
    
    [System.Diagnostics.CodeAnalysis.SetsRequiredMembers]
    public S2(int i = 42)  => v = i;
}

With this change in place, instead of silently getting an instance of S2 initialized with zero (as opposed to the expected 42 if the constructor were to be invoked), we get the following error:

error CS9035: Required member 'S2.v' must be set in the object initializer or attribute constructor.

Not perfect, given that the message will probably be very confusing if one is (incorrectly, but understandably) expecting S2 constructor to be invoked, but there are other limitations rendering this approach even less viable:

  • Inability to detect constructors not being invoked (at least) in default expression and array instantiations.
  • Even in scenarios in which this would work it is impossible to guarantee that a ctor will be invoked (for instance, if we change the code at line #1 to new S2() { v = 5 }, no constructor will be invoked but no warning/error will be emitted either)

A more effective alternative (if you deploy your application as a managed one as opposed to AOTing it) to detect such scenarios is by explicitly asserting that struct instances have been initialized4 (either by a constructor or some other means) prior to accessing its members; since the code is being JITed one can implement this in a way users can control whether the check should be enforced or not and have very little (if any) performance impact when enforcing is disabled, as demonstrated below.

using System.Runtime.CompilerServices;

class Driver
{
    static void Main()
    {

        for(int i = 0; i < 100_000; i++)
        {
            var foo = new Foo(); // No constructor invoked... no warnings :(
            Thread.Sleep(100);
            foo.Use();
        }
    }

}

// The struct can be declared in a different assembly also. 
struct Foo
{
    // it is important for the field to be marked as `readonly`
    private static readonly bool _shouldValidate = Environment.GetEnvironmentVariable("VALIDATE_FOO") == "true";

    private int _i;
    private bool _isInitialized;

    public Foo(int v = 1) { _i = v; _isInitialized = true; }

    public void Use()
    {
        Verify();
        System.Console.WriteLine(_i);
    }

    [MethodImpl(MethodImplOptions.AggressiveInlining)] // It is important to ask the JIT to inline this method for the optimization to be applied.
    private readonly void Verify()
    {
        if (_shouldValidate)
        {
            if (!_isInitialized)
            {
                throw new Exception("Foo constructor was not invoked; this may be due an array declaration or ...");
            }
        }
    }
}

The code use the field _shouldValidate to control whether correct initialization4 should be enforced or not (in this case more specifically, if the constructor has been executed). Notice that this field is declared as static readonly; this is very important since with this in place the JIT knowns that, for a given struct instance, once initialized, the field value will never change so it is free to handle _shouldValidate as a constant and to not generate code to check it in the if in line #XX; moreover, in case it is evaluated to false the JIT can remove the whole if statement (whence the close to zero overhead mentioned before).

You can see this JIT magic in action by opening a terminal, creating a console application with the code above and running:

DOTNET_JitDisasm=Use dotnet run -c Release

which:

  1. in unix like OSs, sets the environment variable DOTNET_JitDisasm value to Use and runs dotnet run -c Release.
  2. builds the application in release mode (-c Release), which is requirement for the optimization to be applied.
  3. instructs the JIT to dump the JITed assembly code for the method Use() by setting DOTNET_JitDisasm environment variable accordingly.

When executing that command line you should see zeros (0) and some assembly code being printed to the terminal multiple times; after some iterations you should be able to spot some assembly code resembling the one below (make sure to check the one that contains Tier1 as opposed to Tier0):

; Assembly listing for method Foo:Use():this (Tier1)
; Emitting BLENDED_CODE for X64 with AVX - Unix
; Tier1 code
; optimized code
; rsp based frame
; fully interruptible
; No PGO data
; 1 inlinees with PGO data; 0 single block inlinees; 0 inlinees without PGO data

G_M000_IG01:                ;; offset=0x0000
 
G_M000_IG02:                ;; offset=0x0000
       mov      edi, dword ptr [rdi]
 
G_M000_IG03:                ;; offset=0x0002
       tail.jmp [System.Console:WriteLine(int)]
 
; Total bytes of code 8

which is basically calling System.Console.WriteLine(_i) and returning, with no traces of the method Verify() invocation.

You can also play around with this example by running it as:

DOTNET_JitDisasm=Use VALIDATE_FOO=true dotnet run -c Release

in which case it will throw an exception (proving that uninitialized struct instances usage is detected)

or

DOTNET_JitDisasm=Use dotnet run -c Debug

in which case, no matter for how long the application runs, the assembly code generated for Use() will always call Verify() (i.e. optimization was not applied because application was built in debug mode)

With this approach one can be sure that no code is using uninitialized instances by simply running the code with the environment variable set to true and observing for exceptions.

As always, all feedback is welcome.

Have fun!


  1. here you can find some test code I used while exploring this topic.

  2. After realizing that I've changed the post's title :).

  3. This particular case is problematic due to the expectation that the behavior would match the behavior for classes

  4. Note of clarification: From the perspective of the runtime, structs are guaranteed to be initialized (by zeroing out the whole struct) before being used. Initialization in the context of this series of posts means that all struct fields/properties have been assigned meaningful values leaving the instance in a consistent state.

Feb 27, 2023

Fun with references in C# - Part I / II

Leia este post em português

Lire cet post en Français.

I'll begin 2023 (late February :)) posts with a quick puzzle about C#.

Given the program below answer the following questions:

1. What is its output ?

2. Can you spot/explain the issues in the code?

3. How could one pinpoint the issue?

Of course you are free to compile/run it, and I do encourage you to do so, but I do suggest to only do it after you have formulated an hypothesis or if only inspecting the code does not ring any bells.

See you in the next post.

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

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

Mar 28, 2018

Recursos para se manter atualizado com a linguagem C#

Read this post in English.

Olá

É surpreendente a quantidade de informações que temos a nossa disposição atualmente. Quando eu comecei a estudar programação (a alguns anos atrás :) ), informação sobre programação era muito limitada (morar em uma pequena cidade do interior de São Paulo e não ser capaz de ler nada em Inglês não ajudava muito).

A situação não melhorou muito quando entrei na universidade (3 ~ 4 anos após meu primeiro contato com um clone do Apple IIe); sem dúvida eu tinha acesso a mais livros, mas os mesmos eram em inglês (que eu ainda tinha muita dificuldade em ler por sinal) e também não cobriam uma ampla gama de assuntos da área.

Para nossa sorte (ou não), atualmente temos tanta informação disponível que temos dificuldade para acompanhar as novidades o que nos traz ao tema deste post: Neste mar de informações disponíveis temos que nos concentrar nas que podem nos proporcionar o melhor retorno pelo nosso investimo.

Se tratando da linguagem C#, para mim, do ponto de vista do desenvolvimento da mesma, todos desenvolvedor C# deveriam, pelo menos, conhecer o repositório https://github.com/dotnet/csharplang, o qual se auto denomina, o "repositório oficial para o design da linguagem C#"

Neste repositório você pode encontrar documentação relativa a funcionalidades existentes, e mais importante (em minha opinião), ainda em desenvolvimento (por exemplo, recentemente documentos referentes a algumas 
funcionalides da próxima versão da linguagem (7.3)  foram publicados). 

Lá você também encontra documentos sobre as várias reuniões de design da linguagem.(aqui você pode ver um vídeo, em inglês, descrevendo o processo de design e como você pode usar este repositório).

O mais interessante é que não estamos limitados a apenas consumir passivamente o conteúdo deste o repositório; como o mesmo é aberto qualquer um pode enviar sugestões de novas funcionalidades para a linguagem bem como participar de discussões sobre os vários recursos em desenvolvimento.

Happy coding ;)

Adriano

Keeping up with C# development

Leia este post em português

Hi

it is amazing how much information we have available nowadays. I remember when I started studding programming (some 30 years ago :) ), it was really hard to find information (ok, I used to live in a small city in Brazil and could not read English at all, but still, there were not a lot of information available about computers / programming). 

The situation did not improve much when I went to college (3 ~ 4 years after my first contact with an amazing Apple II clone); surely I had access to more books but unfortunately most of them were in English, and did not cover much ground (at least the ones I had available).

Luckily, nowadays we have plenty of information available (actually much more than what any single person can imagine consuming), which bring us to the topic of this post: As a C# developers we have some great resources at our disposal; from the language development perspective, in my opinion, every C# developer should, at least, be aware  of https://github.com/dotnet/csharplang, in their own words, the "official repo for C# language design"

In that repository you can find documentation about existing and, most interesting, 
under development, language features. As an example, recently design documents for the upcoming C# 7.3 version have been pushed). There we can also find documents for each of the Design Meetings which can be invaluable information for those interested in following the language evolution more closely (here you can find a video describing the design process and how that repo is used).

Notice that you are not limited to consuming information. Since that repository is open, anyone can participate and add his/her own suggestions.

To summarize, if you are interested in following the development of C#, you should definitely keep an eye on that.

Happy codding.

Adriano

Jun 16, 2016

Querying .NET assemblies with LINQPad

Since 2008 I have not only been playing / working with .NET on a daily basis; I've also done quite a lot of work with .NET assemblies and, as result, quite frequently I find myself trying to answer questions like:
  • How many types in the assembly are value types?
  • How many types exposes more than x properties?
  • Do we have any interfaces / types that does not conform to some specific naming convention?
  • etc.
In order to answer these questions I used to either write a quick program (with the help of Mono.Cecil) or ended up having some fun time with regular expressions (of course, running these regular expressions on the source code instead of the built assemblies). These solutions work relatively well but they have some serious drawbacks: i) lots of repetitive work, ii) it is harder to extract information from the source than from assemblies; iii) some times I don't have any source code at all, only the assembly, etc.

Since last Hackweek this was set to change! For some reason I can't explain (it is as if I have been in Mars for the last couple of years ;) I failed to even realize how powerful / flexible LINQPad has become; it is not the case that I was not aware about the tool itself; I knew LINQPad for some time (lets say, for 6 ~ 7 years) but I've never used it util last May; at that time a bunch of team mates were using it to quickly check generated IL for some C# code snippet and since I was participating in the same project I also needed to the same, so I decided to give it a try.

After returning to my daily tasks I sat one day and started experimenting with the idea of running queries on .NET assemblies for a couple of hours, and ended up really happy with the outcome. Actually, it was so easy that I have no idea why it took me so long to start using it: basically we can use .NET reflection and LINQ for Objects (as you can see in the following example):

var a = Assembly.LoadFrom(@"S:\Unity\Editor\Data\Managed\UnityEngine.dll");
var result = from t in a.GetTypes()
             where Char.IsLower(t.Name[0])
             select t.FullName;

result.Dump();

or, in other words, show me all the types with a name starting with a lowercase char!

That is it, simple like that. The best part is that it is plain C# using LINQ and reflection to navigate through the .NET metadata.

Of course there's (almost) nothing specific to LINQPad in that code snippet; to me, the value that LINQPad aggregates lies on how easy and natural it looks like to use it to type such queries without the need to write / compile a separate application and the quick iteration times it enables.

If you want to try it also just copy / past that code to LINQPad, change the assembly path to one that makes sense to you and run (by pressing F5):




Once we have a way to easily run standard LINQ queries over assemblies we can unleash our imagination and rely on LINQPad power. But, before you get too excited, this approach does have one caveat though: you'll need to have a good grasp about .NET reflection to be able to use it effectively.

In my next post I'll write a custom LINQPad Driver in order to use Mono.Cecil instead of .NET Reflection making it even easier / more flexible to use!

Have fun!

Leia este post em Português.

Jan 21, 2016

Finding references to assemblies


The current task at my job requires me to patch (a fancy name for change) .NET assemblies, sometimes adding new members, sometimes changing type references, sometimes messing with the method implementation and sometimes doing a bit of all.

From time to time I need to find out why a assembly (A) has a reference to some other assembly (B); basically what I need to know is which type/member(s) from assembly B is being used by assembly A.

One particularly easy way (IMO) is to simply ildasm assembly A and look for references to B; in order to accomplish that you can do something like:


c:\temp> ildasm /all /out=c:\temp\assembly-a.il assembly-a.dll

After you generated the CIL from your assembly your next step is to search for the name of the assembly you want to find references for (in this example, Assembly_B); you'll find one or more (actually one for each referenced assembly) sessions in the IL code like:

.assembly extern /*23000005*/ Assembly_B
{

}

Now, you can either search for the name Assembly_B or by the number to its left (in this case 23000005)

.assembly extern /*23000005*/ Assembly_B
{
  .ver 1:0:0:0
}
.
.
.

IL_0006:  /* 28   | (0A)0000B2       */ call instance void [Assembly_B/*23000005*/]NamespaceName.TypeName::DoSomething()
.
.
.

In this example assembly A is using the instance method DoSomething() from type TypeName in namespace NamespaceName from assembly B.

What do you think?

Happy coding.

(Leia este post em português)

Encontrando referências para um assembly específico


Atualmente meu trabalho tem envolvido a manipulação / modificação de assemblies .NET, as vezes incluindo novos membros, outras mudando referências para tipos e/ou membros, etc.

Invariavelmente, de tempos em tempos acabo necessitando descobrir porque um assembly específico (vamos chamá-lo de A) possui uma referência para outro assembly (B); basicamente o que tenho que descobrir é quais tipos/membros do assembly B estão sendo referenciados (usados) pelo assembly A.

Existem várias formas de obter tal informação; na minha opinião, a mais simples, é converter, através do aplicativo 
 ildasm que vem junto com o .NET Framework, o assembly em questão (A) para IL e procurar por referências para o assembly B; para tanto você pode fazer algo como:

c:\temp> ildasm /all /out=c:\temp\assembly-a.il assembly-a.dll

Depois de gerar o IL do assembly desejado seu próximo passo é procurar pelo nome do assembly que você deseja encontrar referências (neste exemplo, Assembly_B); você verá uma ou mais (para ser mais preciso, uma entrada para cada assembly referenciado) 
sessões parecidas com:
.assembly extern /*23000005*/ Assembly_B
{

}

Agora você pode tanto procurar pelo nome do assembly (Assembly_B) ou pelo número que se encontra à sua esquerda (neste exemplo 23000005)

.assembly extern /*23000005*/ Assembly_B
{
  .ver 1:0:0:0
}
.
.
.

IL_0006:  /* 28   | (0A)0000B2       */ call instance void [Assembly_B/*23000005*/]NamespaceName.TypeName::DoSomething()
.
.
.


Como você pode notar no trecho de IL acima, no offset 06 do IL existe uma referência ao método DoSomething() da classe TypeName, definida no namespace 'Namespace' no assembly Assembly_B.

Happy coding.

(read this post in english)

Aug 15, 2015

Invalid offsets in IL instructions after modifying assembly with Mono.Cecil

Hi

Since 2008 I've been using Mono.Cecil, an amazing piece of software, that exposes .Net assemblies contents in a relatively easy and intuitive way so one can inspect and/or change the assembly's contents. 

Unfortunately, in order to use Mono.Cecil effectively, you need a fair amount of knowledge about how MS IL works and Mono.Cecil documentation is kind of sparse (to say the least).

Some time ago I was using it to change some assembly IL and to my surprise after applying the changes peverify complained that some IL instructions had invalid offsets

After some head scratching I've figured out that the issue was that the target of a (short) branch instruction have crossed the threshold that would require it to be  a normal branch (i.e, one that could use 32 bits offsets instead of 8 bits of the short version).

So now the issue was that I'd be forced to scan every single IL instruction in the method body and check/fix the target offset of branches; fortunately, Mono.Cecil has  two methods: MethodBody.SimplifyMacros() and  MethodBody.OptimizeMacros() that can be used to achieve my goal. 

Basically before start doing changes to the method body's IL, you call SimplifyMacros() and when you've finished with your changes on that method body you call OptimizeMacros() and Cecil will take care of adjusting branches accordingly. Nice!


Thanks everybody that helped to develop Mono.Cecil! It's really a handy library! :)

(Leia este post em português)

Instruções com offsets IL inválidos após modificar assembly com Mono.Cecil

Desde 2008 venho usando a biblioteca (muito boa diga-se de passagem) Mono.Cecil que permite você tanto ler quanto modificar o conteúdo de assemblies .Net de uma forma relativamente simples (depois que você compreende como utilizá-la, pois infelizmente,  usar tal biblioteca de forma efetiva exige um bom conhecimento sobre como o MS IL funciona e a documentação do Mono.Cecil deixa a desejar).

Algum tempo atras, um dos testes de um dos meus aplicativos (que usa esta biblioteca) começou a falhar; para ser mais preciso peverify começou a reportar algumas instruções com offsets inválidos

Após investigar por algum tempo concluí que o problema se encontrava no offset (operando) usado em alguma instruções de desvio (branch), as quais estavam ultrapassando o limite de um byte (-127 / 128) (isto ocorria devido a dois motivos: i) o código IL usava a forma (short) branch, ou seja, a instrução de desvio utilizada aceitava um único byte como offset e ii) meu aplicativo adicionava instruções entre a instrução de desvio e o alvo do desvio efetivamente exigindo um offset maior que 128).

Munido desta nova informação, tudo que tinha que fazer agora era verificar instrução por instrução (é claro, apenas as de desvio) se o operando da mesma estava dentro da faixa válida (-127 a 128) e corrigir quaisquer uma que não esteja. É claro que eu queria evitar isto a qualquer custo, pois este processo adicionaria mais código (e possíveis bugs) a meu aplicativo. 

Felizmente, Mono.Cecil possui dois método (MethodBody.SimplifyMacros() e  MethodBody.OptimizeMacros()) que, quando usados, se encubem em garantir que os operandos (offset) das instruções de desvio estão dentro das faixas válidas e caso não estejam, trocam a instrução para uma instrução de desvio que suporte o offset em questão (neste  case uma que use 4 bytes).

Basicamente, antes de iniciar qualquer modificação nas instruções (IL) de um método, você executa o método SimplifyMacros() e quando finalizar suas modificações você executa OptimizeMacros() e Mono.Cecil se incumbirá em ajustar as instruções de desvio (se necessário).


Desenvolvedores do Mono.Cecil: meu muito obrigado! :)

Have fun

(Read this post in english)

Aug 7, 2015

Resolução de métodos sobrecarregados (overloaded) em C#

Depois de um longo e tenebroso tempo em silêncio, eu voltei :)

Desta vez com um pequeno teste.

Dado o seguinte programa em C# (não importa muito a versão da linguagem), oque você espera ver no console?

using static System.Console;

class Foo 
{ 
 public void Bar(string[] o) { WriteLine("string[]"); } 
 public void Bar(object o) { WriteLine("object"); } 

 static void Main()
 {
  var foo = new Foo();
  foo.Bar(null); 
 } 
}

Responda através dos comentários

Boa diversão!

Read this post in English!

Overload resolution in C#

After a long dark period of silence I am back :)

This time a little quiz.

Given the following C# program, what you expect to get printed?

using static System.Console;

class Foo 
{ 
 public void Bar(string[] o) { WriteLine("string[]"); } 
 public void Bar(object o) { WriteLine("object"); } 

 static void Main()
 {
  var foo = new Foo();
  foo.Bar(null); 
 } 
}

Answers (if any), in comments ;)

Have fun!

Leia este post em Português!

Mar 5, 2015

Co & Contra variance in C#

Hi
Every now and then I find myself thinking about 2 concepts that has “just” been “extended” in C# language version 4.0 ;): co / contravariance. The interesting fact is that it always seems like my brain is going to blow :)

PS: I used the words “just” and “extended” in quotes because version 4.0 of the language has been released in April/2010 (according to Wikipedia) and C# supported covariance (in some way or another) since version 1.0!

So, in order to try to assimilate the concept, once and for all, I decided to do what I believe to be the most effective way to learn, i.e, to try to explain it to others ;) so, if you already master the subject, go away, do something else :) (this will be an informal discussion about these topics. If you want a more formal one, please see this Wikipedia page and possible follow the links).

Basically, as the Wikipedia article (IMO) puts very well, variance refers to how type inheritance affects the relationship of more complex language constructs (such arrays of derived types instead of arrays of the base type, functions returning a derived type instead of a function return the base type, etc)

Take a look in the following piece of C# code:

using System;

namespace CoContraVariance
{
 class Base { }

 class Derived : Base { }

 class Program
 {
  static void Main(string[] args)
  {
   Derived[] derivedArray = new [] { new Derived(), new Derived() };
   Base[] baseArray = derivedArray;

   foreach (var item in baseArray)
   {
    Console.WriteLine(item);
   }
  }
 }
}


Pretty simple, huh?

In line 13 we create an array of Derived objects initialized with 2 objects; then on line 13 we assign this array to a variable declared as an ‘array of Base’ objects and it just works, after all, Derived inherits from Base, and developers expects such assignment to work.

But don’t get too excited about array covariance yet because… it is broken

The problem is that arrays in C# are covariant, pero no mucho. Suppose that in the next version of the program, you were tasked to, after printing the contents to console, replace the first element of the array with an instance of the Base class, so you come up with the following code:

namespace CoContraVariance 
{
  class Base { } 
  class Derived : Base { } 

  class Program 
  {
    static void Main(string[] args)
    { 
      Derived[] derivedArray = new [] { new Derived(), new Derived() }; 
      Base[] baseArray = derivedArray; 

      foreach (var item in baseArray) { System.Console.WriteLine(item); }

      baseArray[0] = new Base();
    }
  }
} 


Piece of cake, if you don’t mind an exception being thrown at line 13. Why? Well, because since arrays in C# are covariant, but pero no mucho, you can handle an array of a derived type as an array of a base class (like our sample) when you are reading from it, but not when you are assigning to it! It is even worse than that: every assignment need to be checked for type correctness so we are paying an extra performance price whenever we assign to any position of the array.

Another C# construct that supports co/contravariance since version 2.0 of the language is “method group to delegate conversions“. As an example, take a look in the following code:

void Foo(Func<Base> f)

{

}

Derived Bar()

{

}

Even though Foo is declared as taking a function that returns a reference to a Base object, it is perfectly valid to call it as:

Foo(Bar);

Why? Well, whatever value “f” (in this case Bar() method) returns to Foo it will be either an instance of Derived or of a sub class of Derived. In any case this object “is an” instance of Base, which Foo() is prepared to handle.

The converse is also valid for parameters (in this case it is called contravariance):

void Foo(Action<Derived> f)

{

}

void Bar(Base base)

{

}

Foo(Bar);

Note that, even though conversions from method group to delegates are co/contra variant since C# version 2.0, generic delegates are not! So, the following code is invalid on C# versions < 4.0

Action<Derived> ad = Bar; // Ok, valid, method group to delegate conversion (contravariance)

Action<Base> ac = Bar;

Action<Derived> ad_error = ac; // Error.

Ok, now, as every good C# dev is aware, arrays implements the IEnumerable<T> interface, so intuitively developers expect that the following change should be supported:


using System;
using System.Collections;
using System.Collections.Generic;

namespace CoContraVariance
{
 class Base { }

 class Derived : Base { }

 class Program
 {
  static void Main(string[] args)
  {
   Derived[] derivedArray = new [] { new Derived(), new Derived() };
   Base[] baseArray = derivedArray;

   foreach (var item in baseArray)
   {
    Console.WriteLine(item);
   }

   IEnumerable<Derived> deriveds = derivedArray;
   IEnumerable<Base> bases = deriveds;
  }
 }
}


But, if you try to compile this sample against C# version < 4.0 you’ll get an error in line 24 similar to:

Error CS0266: Cannot implicitly convert type ‘IEnumerable<Derived>’ to ‘IEnumerable<Base>’. An explicit conversion exists (are you missing a cast?)
This happens because in C# language, prior to version 4.0, generic interfaces were invariant, i.e, given an interface Itf<T> and types Base and Derived (Derived inheriting from Base) Itf<Derived> had no inheritance relationship with Itf<Base> whatsoever!

In C# version 4.0 the language designers introduced co/contra variance for generic interfaces and generic delegates! The first implication for us, is that, if we try to compile our previous sample (the one in which we play with IEnumerable<T>) with such C# compiler version it works! (that happens because MS annotated IEnumerable<T> as covariant).

The second implication for us is that now we can mark our very own interfaces as such!


namespace CoContraVariance 
{ 
  interface IFoo<out T>
  { 
    T GetValue();
  } 

  class Base { }
  class Derived : Base { } 

  class Program
  {
    static void Main(string[] args)     
    { 
      IFoo<Derived> derivedItf = null; 
      IFoo<Base> baseItf = derivedItf; 
    } 
  } 
} 


The first thing that pops out our eyes is the word “out” (no pun intended) besides the generic parameter “T” and that is the way we tell the compiler that the interface IFoo is covariant in T (if you remove this “marker” line 16 becomes invalid again).

Just in case it is not clear yet, covariant type parameters may only be used as the return type of methods / properties; if you try to define parameters of such types the compiler will kindly remind you that this is not valid ;)
The other language construct, called contravariance, allows us to handle generic interfaces / delegates of a base type as interface/delegate to a more derived one (IMO, contravariance is harder to grasp since it looks like it goes against the “normal” inheritance rules).


namespace CoContraVariance 
{ 
  interface IFoo<in T>
  { 
    void DoIt(T value);
  } 

  class Base { } 
  class Derived : Base { }   
  class MoreDerived : Derived { } 

  class Program 
  { 
    static void Main(string[] args) 
    { 
      IFoo<Base> baseItf = null; 
      IFoo<Derived> derivedItf = baseItf;

      derivedItf.DoIt( new Derived() );
      derivedItf.DoIt( new MoreDerived() );
    }
  } 
}


In this sample, when a call to derivedItf.DoIt() is made we’ll be calling baseItf.DoIt(). Since the compiler will enforce that anything we pass to the former inherits either directly or indirectly from Derived and that Derived “is a” Base, the actual method called will get an instance of Base (which is exactly what it expects)!

If you want to read more about it I recommend this excelent, in deph, series of posts and also the following links:

http://msdn.microsoft.com/en-us/library/ee207183.aspx

http://msdn.microsoft.com/en-us/library/dd799517(v=vs.110).aspx

http://en.wikipedia.org/wiki/Covariance_and_contravariance_(computer_science)

Happy codding.

Apr 23, 2014

C# 6.0: Open Source!

Olá
No post anterior eu comentei sobre algumas das possíveis funcionalidades da próxima versão do C#.

No evento Build 2014 (que ocorreu algumas semanas atras) a MS confirmou algumas destas funcionalidades bem como apresentou algumas que eu não tinha conhecimento (e que realmente gostei). Abaixo segue uma discussão das mesmas, começando por aquela que mais me animou:

Projeto Roslyn

A MS não apensas confirmou que a próxima versão dos compiladores C#/VB.Net serão baseados neste projeto, como surpreendeu muita gente ao anunciar que o mesmo agora é um projeto open source! Isso mesmo, você ouviu correto. Você já pode baixar o código fonte e ver com seus próprios olhos (inclusive muitas das funcionalidades anunciadas para a próxima versão do C# já funcionam com o compilador implementado baseado neste projeto).

Com esta abertura, subtamente, um monte de informação se tornou disponível. Por exemplo, você pode ver aqui a lista completa de funcionalidades para a próxima vesão do C# / VB.NET.

Apesar de, na minha opnião, baixar o código fonte e estudar o mesmo seja o caminho para aprender sobre o Roslyn, caso você não tenha tido contato com o mesmo anteriormente eu recomendo baixar/instalar o End user preview (na minha opnião, um caminho muito mais simples).

Propriedades automaticas (somente leitura)

Implementado; a medida que eu uso mais estes novos recursos eu gosto cada vez mais dos mesmos! :) Veja o exemplo abaixo (especial atenção à linha 3):
class Test(string name)
{
 public string Name { get; } = name;

 public static void Main(string[] args)
 {
  System.Console.WriteLine(new Test(args[0]).Name);
 }
}

“Primary Constructors”

Melhor do que eu esperava. Não apenas o comportamento dos parâmetros definidos em primary constructors obedecem as regras de escopo atuais como também é possível obter o resultado que eu propus no post anterior, ou seja, o conceito de “campos automáticos”. Veja o exemplo abaixo:

class Example (private int value, string n)
{
 private string name = n;

 public static void Main(string[] args)
 {
  new Example(42, "foo").Print();
 }

 private void Print()
 {
  System.Console.WriteLine("{0} = {1}", name, value);
 }
}
Observe que o campo value (usado na linha 12) nunca foi declarado explicitamente (pelo menos não usando-se a sintaxe tradicional)

“using” para membros estáticos (ou static using)

Lembra-se que eu comentei que não fiquei muito empolgado com esta funcionalidade? Pois é, depois de usá-lo bastante (ao escrever os exemplos deste post) estou começando a mudar de idéia ;)

Declaração inline para parâmetros out

Mais um uau! Acontece que meu entendimento da funcionalidade estava incorreta! Na realidade o nome correto da mesma é “declaration expressions” (desculpa, não sei uma boa tradução sem entrar em detalhes sobre conceitos de compiladores, o que não é meu objetivo neste post - além de não conhecer o assunto o suficiente).

Na prática, nesta nova versão, podemos declarar variáveis, por exemplo, na expressão de testes de if s, for s, etc.

Veja o exemplo abaixo:

using System.IO;
using System.Console;

class Test
{
 public static void Main(string[] args)
 {
  if (args.Length == 0)
  {
   WriteLine("arquivo?");
   return;
  }
  
  using(var arq = File.OpenText(args[0]))
  {
   while ( (var line = arq.ReadLine()) != null )
   {
    WriteLine("> {0}", line);
   }
  }  
 }
}
Note que na linha 16 declaramos a variável line na expressão de teste do bloco while!

Como sempre, quando bem utilizado, este recurso tem o potencial de eliminar “ruido” do código.



Funcionalidades que eu não estava ciente (e não comentei no post anterior)

Separadores em literais e constantes binárias

Ok, essa é mais uma “me too” copiado do Java ;)
Confesso que torci o nariz quando vi a funcionalidade implementada no Java, mas após usá-la algumas vezes acabei me convencendo da conveniência da mesma (infelizmente não esta implementado nesta preview ainda).

Basicamente, agora podemos usar o separador _ em valores de constantes além de declaras constantes binárias. Veja o exemplo:

class Test
{
 public static void Main(string[] args)
 {
  var value = 0xDEAD_BEEF;
  var bin = 0b00000100;
 }
}

Exception Filters

Na versão corrente do C# (5.0), cenários que demandam tratamento específico de exceções baseados em dados da mesma acabam sendo um tanto quanto difíceis. Imagine o exemplo abaixo:
class Example
{
 public void M()
 {
  try 
  { 
  } 
  catch(MyException me) 
  { 
   if (me.SomeProp == some_value) 
   { 
   // trata a exceção aqui 
   } 
   else 
   { 
    throw; 
   } 
  }
 }
}
O if dentro do bloco catch (linha 10) tem como único objetivo filtrar exceções baseado no valor de uma propriedade. A partir da versão 6.0 do C# a seguinte sitaxe é válida:
class Example
{
 public static void Main()
 {
  try 
  { 
  } 
  catch(MyException me) if (CheckException(me))
  { 
   // trata a exceção aqui 
  }
 }

    private static bool CheckException(MyException ex)
    {
        return ex.SomeProp == 42;
    }

}

class MyException : System.Exception
{
 public int SomeProp;
}
Eu, particularmente gostaria de poder usar uma sintaxe mais parecida com a declaração de uma expressão lambda:
class Example
{
 public static void Main()
 {
  try 
  { 
  } 
  catch(MyException me => me.SomeProp == 42)
  { 
   // trata a exceção aqui 
  }
 }
}

class MyException : System.Exception
{
 public int SomeProp;
}

Membros indexados

Esta funcionalidade tem como objetivo simplificar o processo de inicialização de tipos que sobrecarregam o operador de indexação [] introduzindo uma sintaxe alternativa, mais natural em alguns cenários:
using System.Collections.Generic;
using System.Console;

class Example
{
 private IDictionary<string, int> data = new Dictionary<string, int>();

 public int this[string i] 
 { 
  get { return data[i]; }
  set { data[i] = value ; }
 }

 public static void Main(string[] args)
 {
  var dic2 = new Dictionary<int, string>() 
  { 
   {0, "nada" },
   {1, "uno" }
  };

  var dic = new Dictionary<int, string>() { [0] = "nada", [1] = "uno" };   
  var inst = new Example { $zero = 0, $one = 1, $two = 2 };

  WriteLine("Hello World from roslyn! {0} {1}", inst.$zero, inst["zero"]);
 }
}
Outro ponto positivo é que esta sintaxe é válida também na inicialização de objetos.

Note que, além da sintaxe simplificada quando o operador de indexação utiliza strings, também pode-se utilizar a sintaxe de indexadores (quando a chave não é string).

Recurso muito bem vindo em minha opinião.

Extension “Add” Methods em inicializadores de coleções


Caso você nunca tenha usado “collection initializers” em seus programas veja o exemplo abaixo:

using System.Collections;

class Coll : IEnumerable
{
 public void Add(string s)
 {
  System.Console.WriteLine(s);
 }

 public IEnumerator GetEnumerator() { return null; }
}

class Test
{
 public static void Main(string[] args)
 {
  var x = new Coll() 
  { 
   "foo",
   "bar",
   "baz" 
  };
 }
}
A existência do método Add() (linhas 5 ~ 8) (bem como implementar IEnumerable) é uma condição indispensável para que a syntaxe de “Collection Initializers” (linhas 17 ~ 22) possa ser utilizada. Até a versão 5.0 (inclusive) do C# este método tinha que, obrigatóriamente, ser um método na hierarquia da classe.

A partir da versão 6.0 do C# Extension Methods também são suportados:
using System.Collections;

static class Extensoes
{
 public static void Add(this Coll coll, string s)
 {
  System.Console.WriteLine(s);
 }
}

class Coll : IEnumerable
{
 public IEnumerator GetEnumerator() { return null; }
}

class Test
{
 public static void Main(string[] args)
 {
  var x = new Coll() 
  { 
   "foo",
   "bar",
   "baz" 
  };
 }
}

Outras funcionalidades planejadas

Dentre as funcionalides comentadas no post anterior que ainda não foram implementadas mas que estão planejadas podemos citar:

  • Property Expressions / “Method expressions”: Na documentação do projeto aparecem com o nome de Expression-bodied members.
  • Enumerables como parâmetros do tipo “params”
  • Inferência de tipos a partir de parêmtros de construtores

Você pode ver a lista completa das funcionalidades planejadas / já implementadas para as próximas versões do C#/VB.NET nestes links.

O que você acha? (eu, particularmente, estou entusiasmado com o futuro da plataforma .Net!)

Mar 26, 2014

Possíveis recursos do C# 6.0






Possiveis funcionalidades do C# 6.0


Sei que alguns desenvolvedores verão este post como notícia velha, mas eu me deparei com estas informaçoes hoje e as achei bem interessantes e acredito que estas podem ser úteis a mais desenvolvedores (mesmo porque não encontrei muito sobre o assunto em português), assim sendo, decidi postar sobre o assunto.


Se você deseja ver os posts originais (em inglês) veja estes links (ambos são baseados em um evento da Microsoft chamado NDC que ocorreu em Londres Dez/2013).


Os comentários representam a minha opnião (que pode não representar nada para você :)) e eu não sou, de forma alguma, um especialista em linguagens de programação (por exemplo não fiz esforço algum para verificar se as minhas sugestões não introduziriam ambiguidades na sintaxe da linguagem).


“Primary constructors”


class Exemplo
{
 public Exemplo (int valor, string nome)
 {
  this.valor = valor;
  this.nome = nome;
 }

 private int valor;
 private string nome;
}
C# 5.0


class Exemplo (int valor, string nome)
{
 private int valor;
 private string nome;
}
C# 6.0


Os seguintes pontos não ficaram claros para mim:

- será possível ter um corpo no construtor ?

- paramêtros com valor default (possivelmente sim) ?

- qual a visibilidade deste construtor? Assume a visibilidade da classe ?

- será possivel definir outros construtores ?


Já que o objetivo é simplificar porque não introduzir um conceito tipo “campos automáticos” ou mesmo “propriedades automáticas” permitindo assim reescrever o exemplo como:


class Exemplo (int valor, string nome) // Isto definiria os campos valor e nome.
{
}
C# 6.0


Propriedades (automaticas) somente leitura


class Exemplo
{
 public int PropriedadeSomenteLeitura { get; private set; }
}
C# 5.0


class Exemplo
{
 public int PropriedadeSomenteLeitura { get; } = um_campo_qualquer * UmMethodQualquer(1.0);
}
C# 6.0


Eu realmente não vejo muito ganho com isto. Prefiro a versão abaixo (já suportada atualment é claro)

(ok, eu sei que as mesmas não são semanticamente equivalentes, mas param mim, isto já é o suficiente)


class Exemplo
{
 public int PropriedadeSomenteLeitura { get; private set; }
}
C# 5.0


Outra questão é: o que pode ser usado como valor da propriedade? Qualquer expressão? Por exemplo, o código abaixo é válido?


class Exemplo
{
 public int PropriedadeSomenteLeitura { get; } = um_campo_qualquer * UmMethodQualquer(1.0);
}
C# 6.0


“Using” para membros estáticos


class Exemplo
{
 public double M()
 {
  return System.Math.Sqrt(1.0, 4.2);
 }
}
C# 5.0


using System.Math;

class Exemplo
{
 public double M()
 {
  return Sqrt(1.0, 4.2);
 }
}
C# 6.0


Sério? Java já suporta este recurso a algum tempo… e eu nunca gostei muito do mesmo… ;)


Property Expressions (não sei nem como traduzir isso)


class Exemplo
{
 public decimal ValorTotal { get { return Quantidade * ValorUnitario ; } }
}
C# 5.0


class Exemplo
{
 public decimal ValorTotal => Quantidade * ValorUnitario;
}
C# 6.0


  • Na minha opnião vivemos muito bem sem este recurso.

  • Péssimo nome (talvez Property Shortcut seria um nome mais descritivo)


“Method expressions”


class Exemplo1 
{ 
 public Point Move(int dx, int dy) 
 { 
  return new Point(X + dx, Y + dy); 
 } 
}
C# 5.0


class Exemplo1 
{ 
 public Point Move(int dx, int dy) => new Point(X + dx, Y + dy); 
}
C# 6.0


Mesma coisa que “property expressions”


Enumerables como parâmetros do tipo “params”


class Exemplo
{
 public void FazAlgo(params []MeuTipo items) { }
}
C# 5.0


class Exemplo
{
 public void FazAlgo(params IEnumerable<MeuTipo> items) { }
}
C# 6.0


Sem dúvida alguma, qualquer desenvolvedor que já tenha escrito métodos usando número de parâmetros variáveis (identificado pela palavra reservada params) sabe o quanto o mesmo é útil. Suportar IEnumerable<T> além de arrays torna os mesmos muito mais versáteis (pode-se passar qualquer coleção padrão!)


Dessa eu gostei pois não me obriga criar arrays só para poder chamar métodos como no exemplo acima (lembre-se que em C# arrays implementam IEnumerable<T> então, passar arrays, para métodos definidos como o exemplo acima, não exige conversões!


Checagem de “null” mais inteligente (ou “monadic null checking”, o que quer que isso queira dizer ;)


class Exemplo
{
 public int M(IList<string> ss)
 {
  if (ss != null)
  {
    var primeiraOuNulo = ss.FirstOrDefault();
    if (primeiraOuNulo != null)
     return primeiraOuNulo.Length;
  }
  return -1;
 }
}
C# 5.0


class Exemplo1
{
 public int M(IList<string> ss)
 {
  return ss?.FirstOrDefault()?.Length ?? -1;
 }
}
C# 6.0


Vamos acabar com o Natal! (afinal de contas, este tipo de sintaxe ajudaria em muito a por um fim em código tipo arvore de natal). Eu conheço alguns desenvolvedores que ficaram muito desapontados se este recurso for realmente incorporado à linguagem - já posso ver os argumentos de que agora o código esta mais complexo, não é trivial ;)


Ponto para a equipe do C#.


Inferência de tipos a partir de parêmtros de construtores


void M()
{
 var l = new List<string> { "Lista", "de", "strings" };
}
C# 5.0


void M()
{
 var l = new List { "Lista", "de", "strings" };
}
C# 6.0


Preciso dizer algo? Se você já desenvolveu aplicativos em C# com um nível de complexidade acima do trivial sabe o quanto é tedioso ficar lembrando o compilador sobre informações que ele já tem (ou que poderia facilmente obter). Mais um ponto para os designers da linguagem.


Alguns vão dizer que a linguágem X ou Y é muito melhor no quesito inferência de tipos; pode até ser verdade, mas eu uso o C# no dia-a-dia, não estas linguaguens ;), assim sendo, melhorias no sistema de inferência de tipos são bem vindas.


Declaração inline para parâmetros out


void M()
{
 int valor;
 if(Int32.TryParse("42", out valor))
  return valor;

 return -1;
}
C# 5.0


void M()
{
 return Int32.TryParse("42", out int valor) ? valor : -1;
}
C# 6.0


Hum.. este recurso me deixou em dúvida; embora eu entenda que esta mudança tem o potencial de diminuir a quantidade de código em certas construções comuns em programas (métodos no estilo TryParseXXXX() me vem a cabeça imediatamente) ainda receio que o ganho proporcionado seja menor que o risco de mau uso.


Compilador como serviço


De acordo com Anders Hejlsberg (neste vídeo +- 00:34), é possível que o projeto conhecido como Roslyn também seja liberado na próxima versão do Visual Studio! Se você ainda não deu uma olhada no mesmo, eu recomendo dedicar alguns minutos (possivelmente algumas horas :)) para fazê-lo! Outra solução semelhante é o NRefactory.


Agora que já discuti as possíveis funcionalidades da próxima versão do C# (pelo menos, segundo os posts que eu encontrei), gostaria de expor minha lista de desejos pessoal.


Funcionalidades que gostaria de ver na próxima versão do C#


Classes anônimas implementando interfaces


Se você já desenvolveu em Java provavelmente já fez uso deste recurso e possivelmente sabe o potencial para diminuir a quantidade de código escrito bem com simplificá-lo. Sinceramente, este é uma das poucas funcionalidades de Java que eu sinto falta no C#. Se você não tem idéia do que estou falando veja o exemplo abaixo:


public interface IFoo
{
 void DoIt();

 string Name { get; }
}

class Exemplo
{
 public void FazAlgo()
 {
  var itfImpl = new IFoo
  {
   void DoIt() { }

   string Name { get { return "name"; } }
  }
  
  System.Console.WriteLine(itfImpl.Name);
 }
}
Proposta


Em uma versão hipotética do C#, nas linhas 12 ~ 17 implementamos a interface IFoo anonimamente (ou seja, sem a necessidade de definir uma classe explicitamente).


Extension properties


Da mesma forma que extension methods podem simplificar, e muito, algums cenários de programação, acredito que o mesmo conceito aplicado à proriedades seria bastante útil (pelo menos eu já me deparei com algumas cituações onde o mesmo seria realmente muito bem vindo).


static class StringExtensions
{
 public static extension bool ContemAdriano
 { 
  get 
  {
   return value.Contains("Adriano");  // value representa a string.
            // Em nosso exemplo de chamada 
            // value representa o parâmetro 's'
  } 
 }
}

class Exemplo
{
 public bool FazAlgo(string s)
 {
  return s.ContemAdriano;
 }
}
Proposta


Interpolação de strings


Interpolação de strings permite a substituição de uma (ou mais) parte(s) de uma string pelo valor de uma variável de uma forma simples e intuitiva. Abaixo apresento um exemplo hipotético caso o conceito fosse suportado em C#:


class Exemplo
{
 public void FazAlgo()
 {
  int valor = 10;
  System.Console.WriteLine("Valor = $valor, Nome= $nome");
 }

 public string nome;
}
Proposta


O mesmo exemplo, hoje, pode ser escrito como:


class Exemplo
{
 public void FazAlgo()
 {
  int valor = 10;
  System.Console.WriteLine("Valor = {0}, Nome={1}", valor, nome);
 }

 public string nome;
}
C# 5.0


Apesar de algumas reservas, devido ao potencial de confusão em casos onde strings referenciem variáveis declaradas em um escopo distante, gostaria de ver esta funcionalidade na próxima versão do C#!


Modernização de algumas APIs da FCL


Ok, este ponto não tem nada a ver com a linguagem C# propriamente dita, mas já que estou reclamando, não custa nada incluir mais um item na lista de reclamações ;)


Eu admito: eu amo LINQ, e estou certo de que você, caso desenvolva aplicativos de tamanho razoável, também aprecia a elegância desta tecnologia; contudo, muitas das APIs da FCL não estão otimizadas para uso com LINQ. Um exemplo é a classe Regex e relaciondas (recentemente tenho usado as mesmas com bastante frequência); com mais frequencia do que eu gostaria eu me vejo escrevendo cógido como o exemplo abaixo:


var r = Regex.Matches("07/11/1990", "(?<componente>\d{2,4})+");
r.Groups.OfType<Group>().Where(...);

só porque a propriedade Groups é declarada como GroupCollection ao invés de IEnumerable<Group> ou algo parecido. Este é apenas um exemplo; existem muitas outras APIs que se beneficiariam se fossem refatoradas com LINQ em mente.


Antes que alguém reclame e me diga que eu posso implementar os operadores LINQ mais comuns esperando GroupCollection como parâmetro ao invés de IEnumerable<Group> gostaria de lembrá-lo que este é apenas um dos casos; só na família de classes relacionadas à regular expressions temos GroupCollection, CaptureCollection e MatchCollection; existe uma boa probabilidade de seu código depender de outras APIs da FCL que implementão coleções não padrão e, se isso ocorre, você terá que implementar operadores LINQ para elas também.


O que você acha de tudo isso? Quais funcionalidades você gostaria de ver implementadadas na próxima versão da linguágem?


Boa programação!