FluentIL – Parte 9 – Suporte para propriedades automáticas

Posted on 20/09/2011

1


Olá pessoal, tudo certo?

Depois de três meses, retomo esse projeto (consulte os posts anteriores para “pegar” o projeto). Para quem está chegando agora, FluentIL é uma espécie de helper para programas que necessitam fazer emitting. Naturalmente, para conseguir usar Emitting, você precisa saber Intermediate Language (há uma série extensa sobre esse assunto aqui no blog).

Hoje, vou adicionar suporte a propriedades automáticas. Para entender mais sobre como propriedades são representadas no “core” do .NET, consulte o post sobre o tema na minha série sobre IL. Para saber como fazer o emitting de propriedades automaticas, consulte o post sobre o tema na minha série sobre como criar Proxies automáticos.

Lembre-se, para todo o código fonte está disponível no meu github.

Começando com um teste

Antes de fazer qualquer alteração em nosso código, vamos escrever testes:


              
using System;

namespace FluentIL.Tests
{
    using NUnit.Framework;
    using SharpTestsEx;

    [TestFixture]
    class PropertyStudies
    {
        [Test]
        public void EmittingTypeThatSupportsAutomaticProperties()
        {
            var newType = IL.NewType()
                .Implements()
                .WithAutoProperty("SomeProperty", typeof(int)
                ).AsType;

            var f = (IFoo)Activator.CreateInstance(newType);
            f.SomeProperty = 10;
            f.SomeProperty.Should().Be(10);
        }

        [Test]
        public void EmittingTypeThatSupportsAutomaticPropertiesWithoutInterface()
        {
            var newType = IL.NewType()
                .WithAutoProperty("SomeProperty", typeof(int)
                ).AsType;

            var f = Activator.CreateInstance(newType);
            var p = newType.GetProperty("SomeProperty");
            p.SetValue(f, 10, null);
            p.GetValue(f, null).Should().Be(10);
        }
        
        public interface IFoo
        {
            int SomeProperty { get; set; }
        }
    }
}

            

Este teste pressupõe a existência do método WithAutoProperty (ainda não implementado). Então, para fazer nosso teste compilar, vamos escrever algum “código”.


              
public DynamicTypeInfo WithAutoProperty(
    string propertyName, 
    Type propertyType
    )
{
    throw new NotImplementedException();
}

            

Compilando! Vamos confirmar nossa Red bar.

image

Perfeito!

Como você pode ver, escrevemos um teste para cenários onde temos o suporte de uma interface, e outro para cenários onde não temos nada.

Implementando o suporte para auto properties

Agora que já temos nosso “desejo” claramente identificado por um teste, vamos escrever código que faça esse teste passar.

Como já indiquei em outros posts, uma auto property, por baixo do capô, não é nada além de uma propriedade comum que armazena seu valor em um atributo de classe. Vamos escrever nosso código:


              
public DynamicTypeInfo WithAutoProperty(
    string propertyName,
    Type propertyType
    )
{
    string fieldName = string.Format("_{0}", Guid.NewGuid());
    this.WithField(fieldName, propertyType);

    var property = this.TypeBuilder.DefineProperty(
        propertyName,
        PropertyAttributes.None,
        propertyType,
        new Type[] { }
        );

    var get_methodinfo = this.WithMethod(string.Format("get_{0}", propertyName))
        .TurnOnAttributes(MethodAttributes.RTSpecialName)
        .TurnOnAttributes(MethodAttributes.SpecialName);

    var get_method = get_methodinfo
        .Returns(propertyType)
        .Ldarg(0) // this;
        .Ldfld(fieldName)
        .Ret();

    property.SetGetMethod(get_methodinfo.MethodBuilder);

    var set_methodinfo = this.WithMethod(string.Format("set_{0}", propertyName))
        .TurnOnAttributes(MethodAttributes.RTSpecialName)
        .TurnOnAttributes(MethodAttributes.SpecialName);

    var set_method = set_methodinfo
        .WithParameter(propertyType, "value")
        .Returns(typeof(void))
        .Ldarg(0) // this;
        .Ldarg("value")
        .Stfld(fieldName)
        .Ret();

    property.SetSetMethod(set_methodinfo.MethodBuilder);

    return this;
}

            

O que esse código faz é bem claro.

  1. criamos um atributo que irá armazenar o valor de nossa propriedade;
  2. criamos um PropertyInfo;
  3. escrevemos os métodos get e set identificando-os como tendo “nomes especiais”;
  4. associamos os métodos a nossa propriedade.

Pronto!

image

Nossos testes passaram!

image

Temos nosso “output” mostrando o IL que nosso código produz.

Por hoje, era isso!

Smiley piscando

Posted in: Sem categoria