image[2]

Esse é o terceiro post sobre como rodar Ruby no .Net, com IronRuby, e integrado ao C#. Estou usando o Visual Studio 2010 que é onde as coisas funcionam mais bem integradas, mas o primeiro post dá pra trabalhar ainda com o Visual Studio 2008. Os posts anteriores estão aqui:

  1. Rodando Ruby com C#: Parte 1
  2. Rodando Ruby com C#: Parte 2 – chamando uma classe Ruby no C#

No primeiro post eu mostrei como configurar o ambiente, e no post seguinte executamos código Ruby via .Net, inclusive passando parâmetros, e pegamos o retorno e utilizamos no C#. Utilizamos classes Ruby no C# normalmente com as novas capacidades dinâmicas do C# 4.

Neste post vou mostrar como fazer o contrário: tenho uma classe no C# que quero ser capaz de chamar a partir do Ruby. Isso é possível, vamos ver como.

Primeiro vou definir a classe que quero chamar do C#. Segue o mesmo molde das classes de antes, ela tem só um método que recebe dois parâmetros, e retorna uma string de acordo com os argumentos recebidos:

public class Teste
{
    public string Chamar(bool flag, string texto)
    {
        return flag ?
            texto + "sufixo" :
            texto;
    }
}

Quero chamar o método “Chamar” desta classe a partir do Ruby. Para poder fazer isso, preciso dizer pra ele referenciar minha dll de alguma forma. No Ruby, a maneira que você usa pra ele acessar outros arquivos Ruby é com a palavra chave “require”. Se você escrever “require MeuArquivo” o Ruby vai tentar encontrar o arquivo “MeuArquivo.rb” nos diretórios especificados na variável $LOAD_PATH, que normalmente inclui o diretório local (.). Pra saber quais são esses diretórios basta inspecionar a variável:

Console Ruby

No IronRuby, além de procurar arquivos .rb, o require vai procurar também dlls. Então você precisa acrescentar uma linha de require com o nome da sua DLL.

O código Ruby então fica assim:

require 'IronRubyNetFX4Test1'
IronRubyNetFX4Test1::Teste.new

Na primeira linha eu referencio meu assembly de testes, que é chamado IronRubyNetFX4Test1.dll. Na linha seguinte eu crio a classe Teste que havia especificado. Notem que os dois pontos duplos :: separaram a especificação do namespace e da classe. Como no Ruby a última linha sempre retorna o resultado, a classe Teste do C# está sendo retornada. O objeto retornado é entendido pelo C# como um objeto dinâmico, e pode ser chamado. Vejam o teste todo:

[TestMethod]
public void ConsigoCriarUmObjetoDotNetEChamarUmMetodoDinamicamenteUsandoONomeCompletoDoTipoEVariavelDinamica()
{
    var source = @"
        require 'IronRubyNetFX4Test1'
        IronRubyNetFX4Test1::Teste.new";

    var scriptSource = _defaultScriptEngine.CreateScriptSourceFromString(source);
    var teste = scriptSource.Execute();
    var resultado = teste.Chamar(true, "ttt");
    Assert.AreEqual("tttsufixo", resultado);
}

Mas eu poderia fazer um cast do retorno, já que o tipo retornado é um tipo .Net, e usá-lo normalmente:

[TestMethod]
public void ConsigoCriarUmObjetoDotNetEChamarUmMetodoDinamicamenteUsandoONomeCompletoDoTipoEVariavelEstatica()
{
    var source = @"
        require 'IronRubyNetFX4Test1'
        IronRubyNetFX4Test1::Teste.new";

    var scriptSource = _defaultScriptEngine.CreateScriptSourceFromString(source);
    var teste = (Teste)scriptSource.Execute();
    var resultado = teste.Chamar(true, "ttt");
    Assert.AreEqual("tttsufixo", resultado);
}

Faz mais sentido, se eu sei que o retorno é um tipo conhecido do C#.

O Ruby tem ainda uma palavra chave que faz a importação do namespace, semelhante ao “using” do C#, é o “include”. E nós podemos usá-la também:

[TestMethod]
public void ConsigoCriarUmObjetoDotNetEChamarUmMetodoDinamicamenteUsandoOInclude()
{
    var source = @"
        require 'IronRubyNetFX4Test1'
        include IronRubyNetFX4Test1
        Teste.new";
    var scriptSource = _defaultScriptEngine.CreateScriptSourceFromString(source);
    var teste = scriptSource.Execute();
    var resultado = teste.Chamar(true, "ttt");
    Assert.AreEqual("tttsufixo", resultado);
}

Com isso, fecho os exemplos do Ruby chamando o C#. Já conseguimos fazer código Ruby chamar C#, e vice-versa. A seguir vou mostrar alguns cenários onde penso que isso pode ser útil. Em breve.


Postado na(s) categoria(s) C# , IronRuby pelo Giovanni Bassi em 11 de janeiro de 2010 às 08:14 | Tags: , ,

IronRuby

Feliz ano novo, pessoal! Vamos começar o ano acelerando.

Volto a falar de IronRuby com C#. Este é o segundo post sobre o assunto, e não vou voltar no básico. Para isso leia o primeiro post, onde mostro como configurar o básico dos testes, baixar e configurar o IronRuby, e preparar o resto do ambiente:

  1. Rodando Ruby com C#: Parte 1

Nesse post vou mostrar como criar uma classe no IronRuby e chamar ela no C# 4 com Visual Studio 2010.

A classe Ruby que quero rodar contém apenas um método, e não faz nada de mais:

class Teste2
    def Chamar(flag, texto)
        if !flag
            texto
        else
            texto + 'sufixo'
        end
    end
end 

A idéia é criar uma instância dessa classe e após declará-la, retorná-la, tudo no mesmo script. No Ruby, a última linha retorna o resultado. Pra isso, então basta incluir:

Teste2.new 

Ok, para fazer isso funcionar, é igualzinho o que é feito no post anterior, que fazia uma soma simples:

[TestMethod]
public void ConsigoCriarUmObjetoRubyEChamarUmMetodoDinamicamente()
{
    var source = @"
        class Teste2
            def Chamar(flag, texto)
                if !flag
                    texto
                else
                    texto + 'sufixo'
                end
            end
        end
        Teste2.new";

    var scriptSource = _defaultScriptEngine.CreateScriptSourceFromString(source);
    var teste = scriptSource.Execute();
    //continua...

A linha 17 está iluminada porque ela é quem executa o código. Adivinham qual o tipo da variável “teste”? Um Ruby de pelúcia pra quem disse “dynamic” (mais sobre ela aqui). No exemplo anterior eu fazia cast para inteiro, porque o resultado era um inteiro, produto da soma de 1 mais 2. E agora o resultado é uma classe que só existe no Ruby, e não existe em lugar nenhum no C#, e portanto não pode ser chamada estaticamente. E o C# é uma linguagem estaticamente tipada, ele não conhece a classe Teste2 declarada no Ruby. Só que na versão 4 ele consegue realizar Dynamic Dispatch, enviando a chamada ao método dinamicamente (mais informação sobre Dynamic Dispatch em uma discussão que tivemos no .Net Architects).

Isso significa que consigo chamar quaisquer métodos e propriedades que existam no objeto Ruby. E é isso que faço:

    //continuando...
    var resultado = teste.Chamar(true, "ttt");
    Assert.AreEqual("tttsufixo", resultado);
} 

Na linha 2 estou executando o método “Chamar”, via dynamic dispatch, que existe na classe Teste2, declarada no IronRuby, e executando seu código. Na linha 3 eu verifico se o resultado era o esperado. O código todo do teste fica assim (apenas repetindo para deixar mais claro):

[TestMethod]
public void ConsigoCriarUmObjetoRubyEChamarUmMetodoDinamicamente()
{
    var source = @"
        class Teste2
            def Chamar(flag, texto)
                if !flag
                    texto
                else
                    texto + 'sufixo'
                end
            end
        end
        Teste2.new";

    var scriptSource = _defaultScriptEngine.CreateScriptSourceFromString(source);
    var teste = scriptSource.Execute();
    var resultado = teste.Chamar(true, "ttt");
    Assert.AreEqual("tttsufixo", resultado);
}

E se eu não quiser escrever o corpo do método Ruby no C#, mas defini-lo em outro arquivo? Não tem problema, o ScriptSource sabe executar arquivos também, além de texto puro. Veja este outro teste, onde faço exatamente isso:

[TestMethod]
public void ConsigoCriarUmObjetoRubyAPartirDeUmArquivoEChamarUmMetodoDinamicamente()
{
    var scriptSource = _defaultScriptEngine.CreateScriptSourceFromFile(
        ObterCaminhoArquivo("RubyTeste.rb"),
        Encoding.UTF8);
    var teste = scriptSource.Execute();
    var resultado = teste.Chamar(true, "ttt");
    Assert.AreEqual("tttsufixo", resultado);
} 

O método CreateScriptSourceFromFile lê o arquivo e executa. O arquivo RubyTeste.rb contém praticamente o mesmo código utilizado no teste anterior. Coloquei ele na estrutura do projeto:

image 

Marquei nas propriedades do arquivo o parâmetro “Copy to Output Directory” como “Copy if newer”, o que garante que o arquivo vai estar no mesmo diretório da dll de testes. O método ObterCaminhoArquivo sabe que deve procurar os arquivos Ruby no diretório especificado, olhando na pasta onde a dll está:

private static string ObterCaminhoArquivo(string arquivo)
{
    return Path.Combine(Path.GetDirectoryName(Assembly.GetExecutingAssembly().Location), arquivo);
}

(Fique atento para colocar o encoding correto no arquivo, no meu caso, UTF8, ou vai dar erro na hora de rodar.)

Para executar o script de definição da classe, mas só criar ela quando quisermos é fácil. Aqui está um teste que faz exatamente isso:

[TestMethod]
public void ConsigoExecutarUmArquivoRubyECriarOObjetoDinamicamente()
{
    var scriptCriaClasse = _defaultScriptEngine.CreateScriptSourceFromFile(
        ObterCaminhoArquivo("RubyTeste2.rb"),
        Encoding.UTF8);
    scriptCriaClasse.Execute();
    var scriptInstanciaObjeto = _defaultScriptEngine.CreateScriptSourceFromString("Teste4.new");
    var teste = scriptInstanciaObjeto.Execute();
    var resultado = teste.Chamar(true, "ttt");
    Assert.AreEqual("tttsufixo", resultado);
}

O arquivo RubyTeste2 tem a definição da classe, mas não cria e retorna, ou seja, o retorno é nulo:

class Teste4
    def Chamar(flag, texto)
        if !flag
            texto
        else
            texto + 'sufixo'
        end
    end
end

Na linha 7 do teste eu executo este arquivo, e na linha 9 eu executo a criação da classe. Como o código está executando sob o mesmo contexto, o mesmo ScriptEngine, a definição da classe ainda está no escopo, ele cria uma instância da classe na linha 9 e retorna.

Pelo que andei pesquisando, há outras maneiras de fazer isso, mas só como o método Execute do ScriptSource, e os métodos CreateScriptSourceFromString e CreateScriptSourceFromFile do ScriptEngine você já faz quase toda a interação básica.

Interessante, certo? Já podemos definir arquivos Ruby e executá-los dinamicamente. Será que o IronRuby consegue ele mesmo criar e interagir com objetos .Net? Não perca as cenas do próximo capítulo.


Postado na(s) categoria(s) C# , IronRuby pelo Giovanni Bassi em 4 de janeiro de 2010 às 08:17 | Tags: , ,

Perguntei essa semana se o correto seria dizer que o C# agora na versão 4 tem parâmetros opcionais, ou argumentos opcionais.

Na especificação do C# aparece das duas formas:

19. Named and Optional Arguments 
Named and optional parameters are really two distinct features, but are often useful together. Optional parameters allow …

Faz diferença?

Na prática a diferença é nula. Conceitualmente há diferença. Parâmetro é o nome dado ao valor onde a declaração da função é feita:

public void int Paginar(int pagina = 1) ...

Nesse caso, pagina é um parâmetro.

Argumento é o nome dado ao valor passado à função:

objeto.Paginar();
objeto.Paginar(1);
objeto.Paginar(pagina: 1);

Nesse caso, pagina é um argumento. Na primeira linha ele não é passado, porque é opcional. Na segunda ele é passado. Na terceira é passado de forma nomeada.

Segundo a Microsoft:

“To communicate this information to the procedure, the procedure defines a parameter, and the calling code passes an argument to that parameter. You can think of the parameter as a parking space and the argument as an automobile. Just as different automobiles can park in a parking space at different times, the calling code can pass a different argument to the same parameter every time that it calls the procedure.”

Essa documentação é do VB, mas o conceito vale para o C# também.

O que acontece então é que o argumento é opcional. O parâmetro apenas é que declara que o argumento é opcional.

E agora também temos parâmetros nomeados. Ou seriam argumentos nomeados? Mais uma vez, os parâmetros sempre foram nomeados, a diferença é que podemos agora nomear os argumentos, de acordo com o nome do parâmetro, e passá-los fora da ordem declarada dos parâmetros no método.

Para saber mais: Argumentos opcionais e argumentos nomeados.

Você não vai ser um desenvolvedor melhor por saber isso, mas serve para quando você ficar sem assunto no próximo happy hour.


Postado na(s) categoria(s) C# pelo Giovanni Bassi em 17 de dezembro de 2009 às 08:22 | Tags: ,

O C# 4 agora suporta parâmetros opcionais. Ou seriam argumentos opcionais?

Você sabe a diferença? Qual é o certo?

Resposta em breve.


Postado na(s) categoria(s) C# pelo Giovanni Bassi em 15 de dezembro de 2009 às 02:17 | Tags:

IronRuby

Agora o C# traz diversas características dinâmicas, na sua versão 4. Falei semana passada como usar uma delas semana passada, com um leitor de XML dinâmico. Além disso, dá pra fazer diversos outros malabarismos com C# 4 e o dinamismo. Já falei disso aqui, aqui, aqui e aqui.

Mas tem uma coisa que não dá pra fazer ainda de maneira muito simples, que é compilar código C# dinamicamente. Até dá pra fazer, mas dá um trabalho violento. Você tem que usar as classes do compilador do C# disponível em Microsoft.CSharp, como a CSharpCodeCompiler, e fazer todo o trabalho de referências, usings, namespaces, e dá um trabalhão que simplesmente não se paga.

O problema vem do fato de o C# não ser uma linguagem que nasceu para ser compilada dinamicamente, aberta desta forma, apesar de estar caminhando para isso. O ideal seria trabalhar uma linguagem que funciona melhor com scripts, mais dinâmica, como o VB. Infelizmente com VB dá o mesmo trabalhão, já que ele é irmão (quase siamês) do C#. Isso não é mais um problema: o .Net agora tem uma linguagem mais adaptada para isso, que é o IronRuby, implementação de Ruby para rodar nativamente no runtime do .Net, assim como o JRuby é a implementação do Ruby para o runtime do Java.

O IronRuby está em versão release candidate e já compila quase toda a especificação do Ruby, rodando os principais frameworks de Ruby, como o Rails (um MVC para web) e o RSpec (framework de BDD). Você pode pegar a versão que funciona com o .Net 3.5, que é o RC1, aqui. Essa versão é numerada como 0.9.3. A versão que vou usar neste post é a 0.9.1, a que tem como o .Net framework 4 Beta 2 (fique atento, há uma versão 0.9.1 que foca no .Net 3.5 também):

Versão do IronRuby

Não vou ficar explicando aqui a sintaxe do Ruby, que é super simples, e tenho certeza que vocês, que gostam de ponto e vírgula e chaves, vão entender. Pra quem quiser ver mais de perto dê uma olhada no tutorial oficial aqui. Eu gosto muito deste tutorial também, e ele é bem mais direto que o oficial.

Vamos ver o que podemos fazer com o Ruby integrado no .Net. Pra começar, baixe a versão que apontei, a que compila com o .Net 4, e baixe, se ainda não baixou (não??), o VS 2010 Beta 2.

Descompacte o zip em C:\ruby. Seu diretório deve ficar assim:

Diretório do ruby 

Não invente de colocar no D:, não invente de colocar debaixo de MyDocuments, ou em qualquer outro lugar. A instalação padrão do Ruby é no c:\ruby, e o Ruby é sensível. Amanhã se você quiser se integrar com a versão original do Ruby, feita em C (chamada de MRI), você vai ter problemas se o IR não estiver neste diretório. Não que não seja impossível colocar em outro lugar, mas se você não gosta de dor de cabeça, acredite em mim, e coloque ele lá, ok? Todos os exemplos do mundo de Ruby em Windows assumem que ele está lá.

Crie um projeto de testes com C# no Visual Studio 2010. Sim, testes. Porque você quer testar o IronRuby, lembra? Nós não testamos com projeto de console, ou Winforms, ou WPF, ou ASP.Net, nós testamos com projetos de testes automatizados, pra ter feedback constante e rápido se fizemos alguma coisa errada. Lembre-se disso, vamos em frente.

Referencie as seguintes dlls do IronRuby e do DLR que vão estar em c:\ruby\bin:

  1. IronRuby.dll
  2. IronRuby.Libraries.dll
  3. Microsoft.Dynamic.dll
  4. Microsoft.Scripting.dll

Em tempo, DLR é o que dá suporte às linguagens dinâmicas e agora faz parte do .Net Framework. O código é aberto e está no Codeplex.

Referencie também Microsoft.CSharp.dll, que deve estar nas suas referências de DLLs .Net. Ele é o assembly que permite usar a palavra chave dynamic, e fornece a infraestrutura de contato com a DLR, entre outras coisas. (Estou esperando desde o Beta 1 essa DLL sumir para dentro do BCL, ou do CLR, mas até agora nada…)

Pronto, infra resolvida. Agora basta fazer os testes.

Crie uma classe de testes, e crie a inicialização do contexto, que basicamente vai consistir em criar o runtime que vai rodar o Ruby. É código padrão, vai ser praticamente sempre igual em toda aplicação. Basicamente a inicialização cria um ScriptRuntime, que define o ambiente de scripting, e então, a partir dele, cria um ScriptEngine, que é quem executa nosso código fonte Ruby. O resto é detalhe. Criei um ScriptEngine estático, que será o padrão, usado por todos os testes. Precisando, podemos criar outro a partir do runtime, se fosse o caso. Ficou assim:

[TestClass]
public class Dado_Um_Contexto_Ruby
{

    private static ScriptRuntime _scriptRuntime;
    private static ScriptEngine _defaultScriptEngine;

    [ClassInitialize]
    public static void E_Um_ScriptRuntime_e_ScripEngine_Padrao_Inicializados(TestContext testContext)
    {
        CriarScriptRuntimeEEnginePadrao();
    }

    public static void CriarScriptRuntimeEEnginePadrao()
    {
        var setup = new ScriptRuntimeSetup();
        setup.LanguageSetups.Add(
            new LanguageSetup(
                "IronRuby.Runtime.RubyContext, IronRuby",
                "IronRuby 1.0",
                new[] { "IronRuby", "Ruby", "rb" },
                new[] { ".rb" }));
        _scriptRuntime = ScriptRuntime.CreateRemote(AppDomain.CurrentDomain, setup);
        _defaultScriptEngine = _scriptRuntime.GetEngine("Ruby");
    }
} 

Com isso, precisamos criar algum código fonte Ruby que execute. Se você abrir a console do IronRuby, disponível no executável c:\ruby\bin\ir.exe, vai poder digitar 1+2 e digitar enter. Vai dar o resultado, assim:

Console do IronRuby

Essa é a instrução mais simples do mundo, uma soma simples.

Que tal testar esse essa instrução? Fica assim:

[TestMethod]
public void Consigo_Somar()
{
    var source = "1 + 2";
    var scriptSource = _scriptEngine.CreateScriptSourceFromString(source);
    var resultado = (int)scriptSource.Execute();
    Assert.AreEqual(3, resultado);
}

Mais simples impossível! Na primeira linha criamos o complicadíssimo fonte, “1 + 2”, que é nosso código Ruby (depois vamos ver códigos mais interessantes, mas esse serve por enquanto). Na segunda linha chamamos o método “CreateScriptSourceFromString” no ScriptEngine e passamos nosso código fonte. Depois executamos, pegamos o resultado e comparamos com o esperado. Rode o teste, vai passar.

Esse é o básico. Nos próximos posts eu vou mostrar pra vocês como criar, com Ruby, um objeto a partir de uma classe definida no próprio Ruby e devolvê-la para o C# manipular, como criar um objeto a partir de uma classe definida em C# e usar ela no Ruby e devolver pro C#, e como chamar métodos de C# para Ruby e vice-versa.

Em tempo, todo esse exemplo rodaria no C# 3. A partir do próximo não vai rodar mais.
E se você gostou do Ruby, e está sentindo falta de uma IDE para te ajudar, dê uma olhada na RubyMine da JetBrains, que é a empresa que faz o Resharper. Já vi o Uncle Bob dizendo que usa, e o Brian Marick, do manifesto ágil, com quem pareei no Ágiles 2009, também usa. Eu tenho usado, e, apesar de não ser um Visual Studio (longe disso), é melhor que o Notepad, ou seus amigos anabolizados como Notepad++, Textmate, etc (que continuam mirrados).

Gostaria de saber se vocês se interessam pelo assunto, ou se encerro ele mais cedo por falta de interesse. Garanto que vou chegar em uma aplicação viável de negócio, mas só mais para o final…


Postado na(s) categoria(s) C# , IronRuby pelo Giovanni Bassi em 14 de dezembro de 2009 às 08:16 | Tags: , ,

Até a versão 3 do C#, não era possível fazer o que vou mostrar aqui. Havia como contornar, mas nada que soasse bem, ou ficasse muito bonito ou fácil de ler.

O objetivo é pegar um arquivo XML, qualquer arquivo XML, com qualquer esquema, lê-lo, e então ser capaz de acessar seus elementos como se fossem referências a outros objetos, e seus atributos como se fossem propriedades primitivas (strings).

Assim, um xml como esse:

<?xml version="1.0" encoding="utf-8" ?>
<raiz propriedade1="p1">
  <no1 propriedade2="p2">
    <no2 propriedade3="p3">
      <no3 propriedade4="p4" propriedade5="p5" >
        <no4 propriedade6="p6" propriedade7="p7" />
      </no3>
    </no2>
  </no1>
</raiz>

Deve ser possível de ser lido de alguma forma, e, no final, me apresentar um objeto que me permita fazer isso:

Assert.AreEqual("p7", leitor.Raiz.No1.No2.No3.No4.Propriedade7);

A questão é que o xml pode mudar, então como definir que os nós devem originar propriedades dinamicamente? Essa era uma limitação do C# (e do VB) até a versão 3 (9 no VB). E, para atender esse tipo de requisito ou utilizávamos um dicionário ou algo parecído, ou tínhamos que cair em linguagens dinâmicas para fazer esse trabalho. Em Ruby isso é feito com algumas magias de metaprogramação bem interessantes. Tudo isso mudou e agora é possível no C# e no VB.

Foi introduzido o conceito de dinamismo no C# na sua versão 4, sobre o qual eu bloguei a mais de um ano (vejam o post principal aqui, e algumas implicações aqui, aqui e aqui) e escrevi um artigo na .Net Magazine, também ano passado. Algumas coisas foram melhoradas de lá pra cá, e uma destas é a que vou mostrar agora.

Antes disso, vamos deixar clara a especificação do que eu quero. Todo mundo sabe que eu trabalho com TDD, então especifiquei o que quero com um teste. Mas esse não é um post sobre TDD, então vou colocar aqui um teste que resume tudo o que eu espero que aconteça:

[TestMethod]
public void Quando_Passo_Um_XML_Simples_Ele_Cria_Um_Objeto_Com_Propriedades_E_Filhos_E_Netos_Complexos()
{
    var xml = @"<?xml version=""1.0"" encoding=""utf-8"" ?>
                <raiz propriedade1=""p1"">
                  <no1 propriedade2=""p2"">
                    <no2 propriedade3=""p3"">
                      <no3 propriedade4=""p4"" propriedade5=""p5"" >
                        <no4 propriedade6=""p6"" propriedade7=""p7"" />
                      </no3>
                    </no2>
                  </no1>
                </raiz>";
    var leitor = LeitorXML.Ler(xml);
    Assert.AreEqual("p1", leitor.Raiz.Propriedade1);
    Assert.AreEqual("p2", leitor.Raiz.No1.Propriedade2);
    Assert.AreEqual("p3", leitor.Raiz.No1.No2.Propriedade3);
    Assert.AreEqual("p4", leitor.Raiz.No1.No2.No3.Propriedade4);
    Assert.AreEqual("p5", leitor.Raiz.No1.No2.No3.Propriedade5);
    Assert.AreEqual("p6", leitor.Raiz.No1.No2.No3.No4.Propriedade6);
    Assert.AreEqual("p7", leitor.Raiz.No1.No2.No3.No4.Propriedade7);
}

Notem que chamo o método estático “Ler” que retorna um objeto e acesso as propriedades deste objeto, que se baseiam no XML. Para ser capaz de fazer isso preciso trabalhar com um objeto dinâmico. O objeto dinâmico que estou usando foi introduzido pela Microsoft recentemente, e permite incluir membros dinamicamente, tanto propriedades quanto métodos. É o ExpandoObject. Na prática, você pode fazer isso:

dynamic o = new ExpandoObject();
o.Nome = "Giovanni";
o.ObterNomeMaiusculo = (Func<string>) ( () => o.Nome.ToUpper());

Assert.AreEqual("Giovanni", o.Nome);
Assert.AreEqual("GIOVANNI", o.ObterNomeMaiusculo());

Na linha 1 eu crio o objeto, e seto ele para dynamic. É importante lembrar de dizer que ele é dinâmico, porque se você declará-lo com var ou tipá-lo como ExpandoObject não vai poder trabalhá-lo dinamicamente. Na linha 2 eu crio uma propriedade, e na 3 eu crio um método. Nas linhas 5 e 6 eu chamo esta propriedade e este método.

O ExpandoObject também implementa um dicionário de string/objeto, onde ele guarda todos os membros (propriedades, métodos, etc) o que permite a você listar estes itens, e incluir e excluir itens de maneira ainda mais dinâmica.

Bem simples, certo?

Agora que vocês já sabem como funciona o ExpandoObject, já até devem saber como fazer o tal do leitor de XML. Aqui está o código:

public class LeitorXML
{
    public static dynamic Ler(string xml)
    {
        dynamic leitor = new ExpandoObject();
        var elementoRaiz = System.Xml.Linq.XElement.Parse(xml);                
        var raiz = CriarObjetoDinamicoAPartirDeElementoXML(elementoRaiz);
        ((IDictionary<string, object>) leitor).Add(elementoRaiz.Name.LocalName.DeixaPrimeiraLetraMaiuscula(), raiz);
        return leitor;
    }
    private static IDictionary<string, object> CriarObjetoDinamicoAPartirDeElementoXML(XElement elemento)
    {
        IDictionary<string, object> filho = new ExpandoObject();
        foreach (var atributo in elemento.Attributes())
            filho.Add(atributo.Name.LocalName.DeixaPrimeiraLetraMaiuscula(), atributo.Value);
        foreach (var elementoFilho in elemento.Elements())
            filho.Add(elementoFilho.Name.LocalName.DeixaPrimeiraLetraMaiuscula(), CriarObjetoDinamicoAPartirDeElementoXML(elementoFilho));                    
        return filho;
    }
}

O ExpandoObject que representa o leitor é criado logo na primeira linha do método Ler. A partir daí, leio os elementos do XML e os transformo em propriedades que apontam para outros ExpandoObjects. Os atributos são transformados em propriedades que retornam strings. Os nomes das propriedades são definidos a partir dos nomes dos elementos e dos atributos. Em 20 linhas tudo foi definido, esse código é suficiente para passar na especificação anterior (o único código que não está aí é um código auxiliar, um método de extensão, usado para tornar a primeira letra maiuscula em cada propriedades, ou seja, é cosmético).

Esse código vai se adaptar a qualquer xml que você gerar, desde que você respeite a convenção: elementos viram referências a outros objetos, atributos viram propriedades primitivas.

Para os que diziam que dynamic não servia pra nada, o que acharam?


Postado na(s) categoria(s) C# pelo Giovanni Bassi em 9 de dezembro de 2009 às 07:34 | Tags:

Vocês viram isso?

Projeto IronRubyInline no Github.

Permite compilar código C#, VB e até F# no meio do código IronRuby.

Não consegui testar ainda, porque está dando pau no download do GitHub, mas sem dúvida parece algo muito legal. Pelo fonte que vi online, ele parece estar usando alguma infra da própria Microsoft. Interessante!

Já começo a pensar em algumas aplicações para isso.

Será que a performance é boa?

Será que funciona?


Postado na(s) categoria(s) IronRuby , C# pelo giovanni bassi em 6 de agosto de 2009 às 04:59 | Tags: , , ,

Respondendo a pergunta desta terça-feira: porque este código está errado?

delegate void MeuDelegate<in T>(T t) where T : struct;

Esse delegate leva a crer que eu conseguiria ser contravariante em tipos de estrutura. Só que contravariância não funciona com structs, e há uma razão para isso: structs não possuem herança. No máximo, podem implementar interfaces.

Dessa forma, se tivesse implementado meu delegate assim:

delegate void MeuDelegate<in T>(T t)

Então, dadas estas classes:

    class C
    {
    }
    class Ca : C
    {
    }

Eu poderia chamar o delegate desta forma:

MeuDelegateCerto<C> umDel = c => Console.WriteLine(c.ToString());
MeuDelegateCerto<Ca> ca = umDel;

Mas isso é impossível com structs, porque eu não posso fazer isso:

    struct S
    {
    }
    struct Sa : S
    {
    }

E portanto, eu também não posso fazer isso:

MeuDelegate<S> outroDel = s => Console.WriteLine(s.ToString());
MeuDelegate<Sa> sa = outroDel;

E isso tudo leva o meu delegate contravariante com structs ser, na verdade, um grande absurdo.

Esse é um dos motivos que acabo usando structs só em lugares bem simples: não tem herança.


Postado na(s) categoria(s) C# pelo giovanni bassi em 23 de julho de 2009 às 09:53 | Tags: ,

Mais uma pegadinha:

Porque esse código está errado?

delegate void MeuDelegate<in T>(T t) where T : struct;

Vou ajudar um pouquinho.

O código acima compila perfeitamente no compilador do C# 4 do Visual Studio 2010, sem nenhum erro ou warning. O "in" presente em <in T> significa que o parametro "t", do tipo "T", é contravariante neste argumento. Variância é algo novo no C# 4, e você pode ler mais sobre ela no blog do Eric Lippert (ou traduzido no blog do Felipe Fujiy).

Como sempre vocês podem me mandar as respostas por comentários, email, ou sinal de fumaça. O melhor é comentários, porque todo mundo consegue ver, e está muito frio para eu ir lá fora observar sinais de fumaça.

A resposta vem na quinta-feira.


Postado na(s) categoria(s) C# pelo giovanni bassi em 21 de julho de 2009 às 02:32 | Tags: ,

Postado na(s) categoria(s) C# , Eventos pelo giovanni bassi em 21 de julho de 2009 às 02:25 | Tags: , ,

Fui convidado pela MSDN e pela TechResult para palestrar sobre C# 4 no evento abaixo, que é amanhã. O Victor Cavalcante também vai estar lá, falando de ASP.Net MVC, além de outros palestrantes também muito bons. Teremos também temas como VSTS, e um painel de arquitetura de software.

O convite está abaixo. As vagas são limitadas, então quer tiver interesse deve se inscrever logo. Vejo vocês lá.

 

 

 

É tempo de atualizar os seus conhecimentos!!!

 

A TechResult Soluções em Tecnologia e Gestão tem o prazer em convidá-lo para o TechDay, um evento técnico sobre as mais novas tecnologias Microsoft na área de arquitetura e desenvolvimento de software.

 

Data: 18/07 (Sábado)

Horário: 10h00min às 17h30min

Local: Centro Universitário SENAC

Avenida Engenheiro Eusébio Stevaux, 823 – Jurubatuba

Centro de Convenções - auditório 2

São Paulo - SP

 

Programação

 

10:00h  Abertura

10:10h  Palestra: Linguagens Dinâmicas e o Browser.

Palestrante: Marcelo D’Ávila de Pauli (MVP), Diretor Executivo da TechResult.

11:00h  Palestra:  Microsoft Innovation Center Curitiba e Programa de Parceiros Microsoft :: Como se beneficiar.

Palestrante: Thiago Henrique Zen, Coordenador Administrativo do MIC Curitiba.

11:20h  Palestra: Conhecendo o Visual Studio Team System

Palestrante: Alexandra Tarifa (MVP), Líder do grupo de usuários Codificando.net São Paulo

 

12:10h  Almoço

 

14:00h  Palestra: ASP.Net MVC: tome seu HTML de volta.

Palestrante: Victor Cunha Cavalcante, Universidade Paulista

15:00h  Palestra: .NET Framework C# 4.0.

Palestrante: Giovanni Bassi. (Microsoft MVP, MCSD, MCPD, CSM)

16:00h  Mesa Redonda: Discussão de Arquitetura .NET.

Mediador: Marcelo D’Ávila de Pauli (MVP), Diretor Executivo da TechResult.

17:20h  Encerramento: Sorteio de Brindes

 

Não perca essa oportunidade!

                         

Vagas limitadas. Confirme sua presença até o dia 17/07 pelo telefone (41) 3335-7612, com Cláudia, ou pelo e-mail eventos@techresult.com.br.

 

Realização

image

 

 

Apoio

 

 

image

image

 

 

 

 

 

:: TechResult Soluções em Tecnologia e Gestão ::

 


Postado na(s) categoria(s) C# , Eventos pelo giovanni bassi em 17 de julho de 2009 às 13:58 | Tags: ,

O código da questão do último post deve ter deixado muita gente de cabelo em pé, e por isso agradeço a todas as contribuições (feitas via Twitter, comentário, ou e-mail). A pergunta era:

Dado o seguinte código:

static int Funcao(int z) { return 1; }
  
static void Main()
{
   dynamic x = 3;
   DateTime y = Funcao(x);
}

O que deve acontecer?

  1. Não compila: tenho um erro em tempo de compilação na linha 6;
  2. Compila e em tempo de execução o inteiro é convertido para uma data usando uma conversão implícita no DLR;
  3. Compila e o inteiro é convertido para uma data usando uma conversão explícita do CLR, mas em tempo de compilação, com IL gerada pelo compilador na chamada da função;
  4. Compila mas tenho um erro em tempo de execução na linha 6, porque não é possível converter um inteiro para uma data.

A resposta correta, nada óbvia, é a quarta. Mas ela só não é óbvia se você não entende ainda como o C# 4 trabalha suas chamadas dinamicas. Vou explicar.

As chamadas dinâmicas se propagam. Quando você passou para o método "Funcao" um argumento dinâmico, toda a chamada passou a ser dinâmica, incluindo o retorno. O que há neste código, na verdade, é uma conversão no retorno de dinâmico para DateTime, porque dinâmico é conversível para qualquer tipo. Só que a conversão é feita em runtime. A "Funcao" sendo chamada parece obviamente ser a que está escrita na linha 1, mas o compilador não tem como saber. Pode ser que houvessem diversos outros métodos com o nome "Funcao" e que um deles retornasse algo que fosse possível converter para uma data. De qualquer forma, ao usar algo dinâmico, você efetivamente abandona qualquer checagem estática de tipos ou de chamadas de métodos em tempo de compilação. Como o argumento x era dinâmico, toda a chamada foi dinâmica.

Depois que entendemos isso fica óbvio. Antes, chega a parecer absurdo.


Postado na(s) categoria(s) C# pelo giovanni bassi em 16 de julho de 2009 às 09:57 | Tags: ,

Vou deixar aqui mais uma pegadinha de C#, desta vez C# 4. Como das outras vezes, não vale usar o Visual Studio 2010 para checar a resposta antes de responder. Depois tudo bem!

Dado o seguinte código:

static int Funcao(int z) { return 1; }
 
static void Main()
{
   dynamic x = 3;
   DateTime y = Funcao(x);
}

O que deve acontecer?

  1. Não compila: tenho um erro em tempo de compilação na linha 6;
  2. Compila e em tempo de execução o inteiro é convertido para uma data usando uma conversão implícita no DLR;
  3. Compila e o inteiro é convertido para uma data usando uma conversão explícita do CLR, mas em tempo de compilação, com IL gerada pelo compilador na chamada da função;
  4. Compila mas tenho um erro em tempo de execução na linha 6, porque não é possível converter um inteiro para uma data.

A resposta vem na quinta-feira.


Postado na(s) categoria(s) C# pelo giovanni bassi em 14 de julho de 2009 às 00:44 | Tags: ,

Estava em um cliente semana passada, quando tivemos um problema na formatação de um número. O número não formatava direito quando chamávamos ToString. Tudo parecia certo. Olhamos todo o código umas dez vezes, e não enxergávamos o problema. Mas havia um problema, nós que não estávamos vendo.

A primeira coisa a fazer era verificar a chamada do método ToString, que estava sendo chamado assim:

var texto = numero.ToString("00");

Queríamos uma formatação de um número com dois dígitos, ou seja, "9" virava "09", "2" virava "02", "49" ficava como estava, e por aí vai. Olhe o código acima. Está certo, não é?

Eu pensei "preciso isolar o problema". A melhor maneira seria abrir uma aplicação console simples e digitar o código lá, e imprimir a saída na tela. Aí pensei "Putz, vou ter abrir o outro VS, criar outro projeto, digitar, verificar, etc." Eu sei. É rápido, mas eu estava sem paciência.

Aí me ocorreu algo. Havia uma maneira mais rápida. Saquei meu console interativo de F#, que fica bem facinho no meu RocketDock (quem precisa de um Mac?):

F# interactive na Rocket Dock

Em menos de um segundos ele abriu. Digitei (clique para ampliar):

F# interactive, criando a função

Enter. Excelente, agora tenho uma função que recebe um inteiro, e devolve uma string formatada como eu queria.

Basta agora digitar:

f 3;;

E enter! Resultado:

F# interactive, resultado da função

Pronto, resolvido. A premissa estava correta. O erro estava em outro lugar. Podia ter feito assim:

F# interactive, chamando uma função direto no número

Ainda mais fácil!

Só tem um problema: eu ainda estou aprendendo F#! Não tenho uma super desenvoltura com a linguagem ainda (ainda!). Queria ser capaz de fazer isso com C#, que é algo que o Anders Hejlsberg já mostrou funcionando no PDC passado (vejam aqui o C# Evaluator), mas que não entrou na versão 4 do C#. Seria tão bom… Enquanto isso, não perca tempo, baixe o F# e ganhe o F# interactive. Ou baixe o IronRuby e ganhe o IronRuby Interactive, que faz a mesma coisa.

(Descobrimos depois. A variável numero não era um inteiro, acho que era uma string, ou um object, não me lembro. Foi só converter o tipo e resolvemos o problema.)


Postado na(s) categoria(s) C# pelo giovanni bassi em 29 de junho de 2009 às 01:14 | Tags: ,

Quem é Giovanni Bassi

Giovanni Bassi Sou uma pessoa apaixonada por tecnologia e especificamente por .Net. Sou consultor independente especialista em .Net, focado em arquitetura e melhores práticas. Tenho dezenas de artigos publicados na .Net Magazine, revista da qual sou editor técnico. Ministro palestras e cursos de vez em quando, e quando dá tempo eu respiro um pouco. Mais detalhes nesta página.

Busca

Selos

MVP

MCPD

MCSD

.Net Magazine

Abaixo ao if!

Calendário

«  março 2010  »
seteququsedo
22232425262728
1234567
891011121314
15161718192021
22232425262728
2930311234
Ver detalhamento de posts no calendário

Blogs interessantes

    OPMLDownload OPML file

    Postagens recentes

    Comentários recentes

    Disclaimer / Aviso
    As opiniões colocadas neste blog são minhas e pessoais e não expressam necessariamente as opiniões de meus empregadores, pareceiros e amigos. Da mesma forma, os comentários feitos por leitores do blog não expressam a minha opinião.

    © Copyright 2010 .Net Unplugged
    Log in