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?
Sucesso! ![]()



Alberto Monteiro
23/09/2011
Elemar,
Os ORM’s como EF e NHibernate criam classes proxy para controlar seu contexto ou sessão.
Esse tipo de abordagem é usando FluentIL?
elemarjr
23/09/2011
Olá Alberto,
Entendi que sua pergunta foi: “ORMs utilizam emitting para criar suas classes proxies?!”
Então, não posso afirmar com certeza se essa é a abordagem adotada. Entretanto, posso relacionar um projeto open-source, indicado a mim pelo @tucaz, que faz exatamente isso.
Dá uma olhada no dapper-dot-net (). Trata-se de “a simple object mapper for .Net”.
O fonte é curtinho e bem instrutivo.
[]s
Elemar JR