FluentIL – Parte 10 – Suporte para propriedades (não-automáticas)

Posted on 22/09/2011

2


Olá pessoal, tudo certo?

No último post dessa série, mostrei minha implementação para emitting de propriedades automáticas. No post de hoje, mostro como realizei a implementação para suportar propriedades “read-only” e com código no getter e setter.

Como de costume, lembre-se que todo o código-fonte está disponível no github.

Testes para propriedades “read-only”

Como de costume, antes de escrever qualquer código novo, vamos escrever algum código de teste:


              
[Test]
public void EmitingTypeThatSupportsReadOnlyProperty()
{
    var newType = IL.NewType()
        .Implements()
        .WithProperty("ReadOnlyProperty", typeof(int), (m) => m
            .Ldc(10)
            .Ret()
            )
        .AsType;

    var f = (IFoo2)Activator.CreateInstance(newType);
    f.ReadOnlyProperty.Should().Be(10);
}

[Test]
public void EmitingTypeThatSupportsReadOnlyPropertyWithoutInterface()
{
    var newType = IL.NewType()
        .WithProperty("ReadOnlyProperty", typeof(int), (m) => m
            .Ldc(10)
            .Ret()
            )
        .AsType;

    var f = Activator.CreateInstance(newType);
    var p = newType.GetProperty("ReadOnlyProperty");
    p.GetValue(f, null).Should().Be(10);
}

public interface IFoo2
{
    int ReadOnlyProperty { get;  }
}

            

Como você pode perceber, assumo aqui a existência de um novo método: WithProperty em nossa interface fluente.

Implementando o método WithProperty para propriedades “read-only”

Agora que já temos nossos testes, hora de implantar o código. Vejamos o que escrevi:


              
public DynamicTypeInfo WithProperty(
    string propertyName,
    Type propertyType,
    Action getmethod
    )
{
    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);

    getmethod(get_methodinfo.Returns(propertyType));

    property.SetGetMethod(get_methodinfo.MethodBuilder);

    return this;
}

            

O terceiro parâmetro refere-se a um action, onde o cliente “realiza” a codificação IL do método (olhe o teste para tornar a coisa mais “evidente”).

Infelizmente, essa implementação também evidencia uma dívida técnica. Muito do código que escrevi está parecido com o que havia escreito no post anterior para propriedades automáticas.

Adicionando suporte para escrita

Já conseguimos adicionar propriedades somente leitura. Agora, queremos suporte para propriedades com suporte para leitura e escrita. Segue a implementação:


              
public DynamicTypeInfo WithProperty(
    string propertyName,
    Type propertyType,
    Action getmethod,
    Action setmethod = null
    )
{
    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);

    getmethod(get_methodinfo.Returns(propertyType));
    property.SetGetMethod(get_methodinfo.MethodBuilder);

    if (setmethod != null)
    {
        var set_methodinfo = this.WithMethod(string.Format("set_{0}", propertyName))
            .TurnOnAttributes(MethodAttributes.RTSpecialName)
            .TurnOnAttributes(MethodAttributes.SpecialName)
            .WithParameter(propertyType, "value");

        setmethod(set_methodinfo.Returns(typeof(void)));
        property.SetSetMethod(set_methodinfo.MethodBuilder);
    }
    return this;
}

            

Essa versão de WithProperty é praticamente idêntica a anterior, porém, agora, aceita também o envio do código para propriedade setter. Esse ajuste autoriza uma revisão do método que escrevemos no post anterior. Observe:


              
public DynamicTypeInfo WithAutoProperty(
    string propertyName,
    Type propertyType
    )
{
    string fieldName = string.Format("_{0}", Guid.NewGuid());
    return this
        .WithField(fieldName, propertyType)
        .WithProperty(
            propertyName,
            propertyType,
            (mget) => mget
                .Ldarg(0) // this;
                .Ldfld(fieldName)
                .Ret(),
            (mset) => mset
                .Ldarg(0) // this;
                .Ldarg("value")
                .Stfld(fieldName)
                .Ret()
                );
}

            

Bem mais expressivo. Vamos aos testes?

image

Sucesso! Smiley de boca aberta

Posted in: Sem categoria