A Post-Easter Egg
One of the hidden features that LinFu.DynamicObject
has is the ability to dynamically add properties and methods to itself using a shared type definition at runtime. In other words, you can have two or more LinFu.DynamicObject
instances share the same DynamicType
, and any changes you make to that type will be propagated to all LinFu.DynamicObject
instances that share that same type:
using LinFu.Reflection.Extensions;
using NUnit.Framework;
namespace LinFu.Reflection.Tests
{
[TestFixture]
public class DynamicTypeTests
{
[Test]
public void ShouldBeAbleToShareTheSameDynamicType()
{
var typeSpec = new TypeSpec() { Name = "Person" };
typeSpec.AddProperty("Age", typeof(int));
var personType = new DynamicType(typeSpec);
var first = new DynamicObject();
var second = new DynamicObject();
first += personType;
second += personType;
IPerson firstPerson = first.CreateDuck<IPerson>();
IPerson secondPerson = second.CreateDuck<IPerson>();
firstPerson.Age = 18;
secondPerson.Age = 21;
Assert.AreEqual(18, firstPerson.Age);
Assert.AreEqual(21, secondPerson.Age);
typeSpec.AddProperty("Name", typeof(string));
INameable firstNameable = first.CreateDuck<INameable>();
INameable secondNameable = second.CreateDuck<INameable>();
firstNameable.Name = "Foo";
secondNameable.Name = "Bar";
Assert.AreEqual("Foo", firstNameable.Name);
Assert.AreEqual("Bar", secondNameable.Name);
}
}
}
Evolving Ducks
Most of the code above is self-explanatory, and the most interesting part about this code is the fact that it has two DynamicObject
instances that share the same DynamicType
instance. Once the Age
property was added to the DynamicType
definition, both first and second DynamicObjects
automatically ‘inherited’ the additional Age
property that was added to the DynamicType
at runtime. Another interesting piece of code was the duck typing call to the IPerson
interface, which wasn’t possible until after the Age
property was added:
IPerson firstPerson = first.CreateDuck<iperson>();
IPerson secondPerson = second.CreateDuck<iperson>();
firstPerson.Age = 18;
secondPerson.Age = 21;
Assert.AreEqual(18, firstPerson.Age);
Assert.AreEqual(21, secondPerson.Age);
As you can see from the example above, LinFu.DynamicObject
is smart enough to change its definition every time the attached DynamicType
definition changes, and that’s why it was also able to duck type itself to the INameable
interface:
typeSpec.AddProperty("Name", typeof(string));
INameable firstNameable = first.CreateDuck<INameable>();
INameable secondNameable = second.CreateDuck<INameable>();
firstNameable.Name = "Foo";
secondNameable.Name = "Bar";
Assert.AreEqual("Foo", firstNameable.Name);
Assert.AreEqual("Bar", secondNameable.Name);
Pretty straightforward, isn't it? (LinFu.DynamicObject
has had this feature for well over 4 years, but I never got around to publishing it until now). Enjoy!