Showing posts with label programação. Show all posts
Showing posts with label programação. Show all posts

Jan 21, 2026

Les structures en C# sont amusantes - Partie 9/9: Conclusion

Leia este post em Português.

Read this post in English.

Dans les articles précédents de cette série, nous avons découvert que, dans certains scénarios, les constructeurs des types valeur (structs) peuvent ne pas être exécutés, malgré une syntaxe qui pourrait laisser penser le contraire.

Être conscient de ce comportement est essentiel, et une attention particulière doit être portée lors de la définition d'API publiques (surtout dans les bibliothèques en raison de leur portée potentielle) afin d'éviter de créer des API contre-intuitives et/ou sujettes aux erreurs, car les développeurs pourraient facilement tomber dans le piège de l'utilisation de membres non initialisés1.

Cela dit, ce comportement n'est pas nouveau ; les problèmes évoqués dans les articles #5, #6 et #7 existent depuis les premières versions du langage. Cependant, l'introduction, en C# 10, de la possibilité de définir des constructeurs sans paramètre dans les structs ajoute une couche de complexité supplémentaire et augmente les chances que les développeurs soient exposés à de tels types2.

Ainsi, si vous consommez des types struct, soyez vigilant afin de ne pas utiliser d'instances non initialisées1. D'un autre côté, faites particulièrement attention lorsque vous définissez de nouveaux types struct, en particulier si vous ajoutez des constructeurs sans paramètres ou un constructeur dont tous les paramètres sont optionnels.

Donc nous arrivons au dernier article de cette série. J'espère que vous avez apprécié ce parcours, que vous y avez appris quelque chose de nouveau et que vous vous sentez désormais mieux préparé(e) à utiliser/définir vos propres structs.

Comme toujours, tout retour est bienvenu.

Amusez-vous bien et à bientôt !


  1. Note de clarification : Du point de vue du runtime, les structs sont garantis d’être initialisés (par une mise à zéro complète de la structure) avant d’être utilisés. Le terme initialisation dans cette série d’articles signifie que tous les champs/propriétés du struct ont reçu des valeurs significatives, laissant ainsi l’instance dans un état cohérent.↩↩

  2. Avant C# 10, la seule façon pour un développeur d'être exposé à de tels types était de consommer des assemblages compilés avec un langage prenant en charge cette fonctionnalité, comme IL ; personnellement, je n’ai jamais rencontré un seul exemple de ce genre.↩

Structs em C# - diversão garantida - Parte 9/9: Conclusão

Read this post in English.

Lire cet article en français.

Nos posts anteriores desta série nós aprendemos que, em alguns cenários, construtores de estruturas (structs) podem não ser executados apesar da sintaxe nos levar a acreditar o contrário.

Ter consciência desse comportamento bem como tomar especial cuidado ao definir APIs públicas (principalmente em bibliotecas, já que as mesmas possuem grande potencial de alcance) é essencial para evitar criar APIs contraintuitivas e/ou propensas a erros uma vez que desenvolvedores podem facilmente ser supreendidos ao usar membros de structs não inicializadas1 introduzindo bugs e/ou falhas de segurança em seus programas.

Dito isso, esse comportamento não é novo, e problemas como os discutidos nos posts #5, #6 e #7 existem desde as primeiras versões da linguagem. No entanto, a introdução da capacidade de definir construtores sem parâmetros em structs na versão 10 do C# adiciona uma camada extra de complexidade e torna mais provável que desenvolvedores sejam expostos a tais tipos2.

Portanto, se você usa tipos struct, garanta que todas as instâncias das mesmas sejam corretamente inicializadas1. Por outro, lado preste especial atenção ao definir novos tipos struct, principalmente se os mesmos possuirem construtores sem parâmetros ou um construtor cujos parâmetros sejam todos opcionais.

Com este post chegamos ao último da série; espero que você tenha gostado da mesma e que possa ter aprendido algo novo na jornada e que agora se sinta mais capacitado para utilizar e expor tais estruturas de forma mais eficiente e segura.

Como sempre, qualquer feedback é bem-vindo.

Divirta-se! Nos vemos no próximo post.


  1. Nota de esclarecimento: Do ponto de vista da runtime, structs são sempre inicializadas (zerando toda a estrutura) antes de serem usadas. No contexto desta série de posts o termo inicialização significa que todos os campos/propriedades da struct receberam valores significativos, deixando a instância em um estado consistente/válido.↩↩

  2. Antes do C# 10, a única forma de um desenvolvedor ser exposto a esses tipos era consumindo assemblies compilados com uma linguagem que suportasse esse recurso, como IL; pessoalmente, nunca encontrei um exemplo desse tipo.↩

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.↩

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.↩↩

Jul 31, 2023

Structs em C# - diversão garantida - Parte 2/9: Uma breve introdução a Value types versus Reference types

Read this post in English.

Lisez cet post en french.

  1. Structs em C# - diversão garantida
  2. Rápida introdução à Value Types vs Reference Types (este post).
  3.  Initialização de campos em estruturas.
  4.  Comportamento de construtores em estruturas.
  5.  Outros cenários em que o comportamento de construtores em estruturas podem te surpreender.
  6.  Argumentos default  em construtores de estruturas (você ainda não esta confuso ?).
  7.  Modificador `required` do C# 11 não vai salvar seu c* trabalho.
  8.  Estruturas usadas como valor default de argumentos.
  9.  Bonus: Evolução das estruturas em C#. 


Desde o lançamento oficial da primeira versão do C# (versão 1.0, em janeiro/2002),  desenvolvedores se deparam com uma decisão ao introduzir novos tipos: declará-los como class (Reference Type) ou struct (Value Type), representados no .NET por System.Object e System.ValueType respectivamente.

Escolher um ou o outro tem implicações não triviais com relação à usabilidade, desempenho e extensibilidade, para listar alguns. Neste post, quero cobrir brevemente as principais diferenças (mais detalhes serão apresentados nos posts futuros se necessário) e esclarecer uma concepção equivocada (a qual provavelmente sou culpado por contribuir para sua disseminassão). Então, sem mais delongas, vamos entrar na distinção mais importante sobre os dois...

Semântia de referencia versus de valor

A característica mais importante que distingue esses 2 tipos está relacionada a como a igualdade e a atribuição/passagem de parâmetro são tratadas. A tabela abaixo mostra essas diferenças (supondo que os tipos em questão não sobrecarreguem o método Equals() ou os operadores ==/!=):


Value Type Reference Type
Assignment semantics
por valor, ou seja, o conteúdo da instância é copiado resultando em duas cópias independentes.
por referência, ou seja, a atribuição apenas copia uma referência e  mudanças através de qualquer uma das referências serão observadas ao acessar o objeto através da outra.
Equality
(Identity semantics)
duas instâncias são iguais se forem instâncias do mesmo tipo e todos os seus campos forem iguais. duas instâncias são iguais se referênciarem o mesmo objeto.

Para facilitar a visualização, imagine o seguinte código:

Durante sua execução, ao chegar na linha 15, podemos representar, de uma forma  simplificada, a memória utilizada para o armazenamento das variáveis v1 e v3 como algo:


ou seja, as variávis v1 e v3 são armazenadas nos endereços 0x100 e 0x400 respectivamente. Observe contudo que a variável v3 não armazena o contéudo da instância do objeto AReferenceType mas sim uma referência (ou, novamente em uma simplificação, um ponteiro) para o objeto instanciado no endereço 0x1000. v1 (uma instância da estrutura  AValueType) por sua vez armazena os dados da instância diretamente (sem indireções).

Se inspecionarmos o estado da memória quando o programa executa a linha 19 observaremos algo como:

Observe que ambas as variáveis (v1 & v3) tem seus conteúdos copiados para as variáceis recem declaradas (v2 & v4) mas como v3 é uma refeence type, copiar seu valor implica copiar a referência (endereço) armazenada na mesma, fato que ficará mais evidente a frente.

Desta forma, ao imprimir os campos IntValue de v1 & v2 o mesmo resultado é produzido; contudo,  após armazenar o valor 71 em v2.IntValue (linha 21) o estado da memória pode ser representado como:


e consequentemente a linha 22 do programa imprimirá os valores 42 & 71 evidenciando que
v1 & v2 são independentes um do outro.

Reference types funcionam de uma forma diferente; ao executar a instrução na linha 25 o valor do campo IntValue das variáveis v3 e v4 são impressos. Para determinar quais valores serão passados para o método Console.WriteLine(), primeiramente o valor de v3 (0x1000) é lido da possição de memória 0x400 (v3) e a seguir, o conteúdo desta posição (0x1000) (ou mais precisamente 4 bytes que compõem um int em C#) é lido, resultando no valor 42; a seguir o memo processo é repetido para a variável v4 e como esta referencia o mesmo objeto que v3 (0x1000) o mesmo resultado é prodizido.

Uma vez que um processo similar é aplicado ao se modificar o conteúdo de um Reference Type, após a execução da linha 26 o estado da memória se parecerá com

 

e a linha 27 imprimirá 71 & 71, ou seja, modificações aplicadas ao objeto referenciado por v4 são observadas através da variável v3 (o que faz todo o sentido já que ambas variáveis referenciam o mesmo objeto).

Concepções equivocadas

Ao longo dos anos, li vários artigos, entrevistas e até alguns livros afirmando que uma das principais diferenças entre Reference Types e Value Types é que Value Types são sempre armazenados na pilha, enquanto Reference Types são sempre armazenados no heap.

Esse equívoco é tão difundido que Eric Lippert (o qual trabalhou como um dos designers de C# no passado) escreveu 2 postagens de blog com o objetivo de  acabar com a confusão.

Como Eric menciona, o fato de Value Type serem normalmente alocados na pilha é um detalhe de implementação. Contudo, na minha opnião este é um detalhe que dificilmente mudará uma vez que esse comportamento é essencial em cenários em que manter baixas as alocações de heap (e, consequentemente, a pressão de GC) é crucial.

Como sempre, todo feedback é bem-vindo.

Have fun!

Jun 30, 2023

Structs em C# - diversão garantida - Parte 1/9

Lisez cet post en french.

Read this post in English.

Mesmo existindo desde a introdução de .NET, Value types (e consequentemente, structs) possuem vários aspectos que ainda em 2023 são uma fonte de confusão (pelo menos para mim). Assim sendo decide fazer uma lista dos comportamentos que podem não ser tão óbvios, os quais abordarei em na série de futuro posts abaixo:

  1. Este post.
  2. Rápida introdução à Value Types vs Reference Types.
  3. Initialização de campos em estruturas.
  4. Comportamento de construtores em estruturas.
  5. Outros cenários em que o comportamento de construtores em estruturas podem te surpreender.
  6. Argumentos default  em construtores de estruturas (você ainda não esta confuso ?).
  7. Modificador `required` do C# 11 não vai salvar seu c* trabalho.
  8. Estruturas usadas como valor default de argumentos.
  9. Bonus: Evolução das estruturas em C#.

atualizando os links acima na medida que postar sobre cada assunto.

Antes de continuar, observe que esta série reflete o estado corrente do C#, ou seja,  a versão 11 (.NET 7.0).

Espero que a mesma possa ajudá-lo, mesmo se você for um desenvolvedor  C# experiente.

Como de praxe, todo retorno é bem vindo.

Have fun!

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

Mar 11, 2016

Mostrando a branch corrente (mercurial) no prompt do ConEmu

Uma das funcionalidades que sempre gostei no GIT é o fato do GitBash (o console do mesmo) mostrar o nome da branch corrente (no exemplo abaixo, o a branch corrente é a ofx):


Não é de se admirar que quando comecei a usar o mercurial este recurso (ou melhor, a falta do mesmo) me causou certa dificuldade / desconforto.

Considerando que tenho uma memória muito ruim e que facilmente costumo ter entre 5 ~ 6 consoles abertos, é fácil entender a razão do porque, por mais de uma vez, eu estar trabalhando em um console assumindo que estava em uma branch qualquer quando na realidade estava em uma branch completamente diferente (não preciso dizer que isso me deu bastante dor de cabeça).

Alguns dias atras tive a ideia usar o conceito de hooks do mercurial para detectar quando eu mudasse de branch e assim setar o prompt com o nome da mesma; infelizmente esta ideia não funcionou muito bem pois hooks do mercurial são executados pelo processo hg.exe que por sua vez é executado a partir do console e assim sendo minhã solução não era viável pois um processo não pode mudar variáveis de ambiente (no caso o prompt) do processo parente, ou seja, o processo que o iniciou  (muito menos do processo parente do processo parente como neste caso).

Minha próxima tentativa foi verificar se o ConEmu (meu console preferido) tinha algum suporte que me permitisse definir o prompt dinamicamente; felizmente ConEmu suporta uma sintaxe estendida de códigos ANSI, mais precisamente $E ] 9 ; 7 ; ”cmd“$E\  o qual permite executar um programa (especificado pela string cmd) e usar a saída padrão do mesmo como parte do prompt. 

Munido destas informações eu simplesmente criei o aquivo bat abaixo:

@echo off
IF NOT EXIST .hg GOTO NOT_A_MERCURIAL_REPO
hg branch
GOTO END

:NOT_A_MERCURIAL_REPO
@echo !repo
:END

e defini a variável de ambiente PROMP como:

SET PROMPT=$E[32m$E]9;8;"USERNAME"$E\@$E]9;8;"COMPUTERNAME"$E\$S$E[1D$S$E[92m$P$E[90m$E[38;5;14m $Q$G $E]9;7;"caminho_do_arquivo_acima.bat"$E\$E[90m$G$E[m$S

e voalá!



Como você pode ver na imagem acima, quando o diretório corrente é um repositório mercurial a string => nome_da_branch_corrente é mostrada no prompt (caso o diretório corrente não seja um repositório mercurial a string !repo é apresentada). 

Como último passo configurei o ConEmu para definir o prompt toda vez que um novo console / tab é criado (para tanto eu simplesmente copiei o conteúdo do arquivo "%ConEmuBaseDir%\CmdInit.cmd" par um aquivo chamado ConEmuInit.cmd e o editei de forma que o memo defina o prompt com o valor desejado):



Agora sim, certamente minas dores de cabeça diminuirão :)

Edit:

Se você estiver usando a versão 160609 ou mais nova do CONEMU então será necessário configurar o mesmo para que o batch que você criou possa ser executado (no meu caso este batch encontra-se em c:\utils\hg_current_branch_or_die.bat)



Happy coding

Read this post in English!

Jan 21, 2016

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)

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!