Skip to content

Instantly share code, notes, and snippets.

Embed
What would you like to do?
Sigil Object Mapping - Basic Examples
// Runnable from Linqpad 5
// Must install the Sigil nuget package and include the Sigil namespace
void Main()
{
var createSample = GetMapperDelegate();
// try it out
createSample(23, "Hello").Dump();
}
static Func<int, string, Sample> GetMapperDelegate()
{
// going to create a dynamic function which takes an int and string and assigns them
// to the properties Id and Name of a new Sample object.
// create emitter for the function
var emit = Emit<Func<int, string, Sample>>.NewDynamicMethod("NewSample");
// create a new Sample object and save it to a local variable
var sample = emit.DeclareLocal<Sample>();
emit.NewObject<Sample>(); // [newobj]
emit.StoreLocal(sample); // empty
// assign Id
emit.LoadLocal(sample); // [sample]
emit.LoadArgument(0); // [sample] [id]
emit.CallVirtual(typeof(Sample).GetProperty("Id").SetMethod); // empty
// assign Name
emit.LoadLocal(sample); // [sample]
emit.LoadArgument(1); // [sample] [name]
emit.CallVirtual(typeof(Sample).GetProperty("Name").SetMethod); // empty
// return the object
emit.LoadLocal(sample); // [sample]
emit.Return();
// we're done emitting IL, actually generate the function now
return emit.CreateDelegate();
// Note, we didn't really need to create a local variable, we could have used Duplicate()
// to duplicate the top stack item every time we needed to use it before the return statemet.
// The example below will use this methodology.
}
public class Sample
{
public int Id { get; set; }
public string Name { get; set; }
}
// Runnable from Linqpad 5
// Must install the Sigil nuget package and include the Sigil namespace
void Main()
{
var createSample = GetMapperDelegate();
// try it out
createSample(new Dictionary<string, string>()
{
["One"] = "1",
["Two"] = "22",
["Three"] = "333",
["Four"] = "4444",
}).Dump();
createSample(new Dictionary<string, string>()
{
["One"] = "Hello",
["Three"] = "I didn't set every property.",
}).Dump();
}
static Func<Dictionary<string, string>, Sample> GetMapperDelegate()
{
// going to create a dynamic function which takes a Dictionary<string, string> and
// for any string properties of Sample where there is a cooresponding key in the
// dictionary, we'll assign the dictionary value to the Sample property value.
// create emitter for the function
var emit = Emit<Func<Dictionary<string, string>, Sample>>.NewDynamicMethod("NewSample");
const int DICTIONARY = 0;
// get all of the public instance properties of sample
var props = typeof(Sample).GetProperties(BindingFlags.Public|BindingFlags.Instance);
// create a new Sample object
emit.NewObject<Sample>(); // [sample]
// use reflection to get the Dictionary<string, string>.TryGetValue method
var paramTypes = new[] { typeof(string), typeof(string).MakeByRefType() }; // only necessary if there are overloads, but still good practice
var tryGetValue = typeof(Dictionary<string, string>).GetMethod("TryGetValue", paramTypes);
// declare local which we'll use for getting the output from TryGetValue
var value = emit.DeclareLocal<string>();
// for each property which is a string and has a setter, create code which looks for
// that value in the dictionary.
foreach (var prop in props)
{
if (prop.PropertyType != typeof(string) || prop.SetMethod == null)
continue;
// check the dictionary for a key matching the property name
emit.LoadArgument(DICTIONARY); // [sample] [dict]
emit.LoadConstant(prop.Name); // [sample] [dict] [propName]
emit.LoadLocalAddress(value); // [sample] [dict] [propName] [ref value]
// TryGetValue has signature "bool (this Dictionary, string, ref string)" so it consumes three values off the stack and pushes one
emit.CallVirtual(tryGetValue); // [sample] [bool valueExists]
// declare a label which will later mark the end of our if block
var endIfLabel = emit.DefineLabel();
// branch to the end of the if block if "valueExists" is false - consumes one stack value
emit.BranchIfFalse(endIfLabel); // [sample]
// inside the if block - now we can assign the found value to
emit.Duplicate(); // [sample] [sample]
emit.LoadLocal(value); // [sample] [sample] [value]
// setter has signature "void (this Sample, string)" so it consumes two values off the stack and pushes nothing
emit.CallVirtual(prop.SetMethod); // [sample]
// mark the end of our if block
emit.MarkLabel(endIfLabel);
}
// we're done assigning properties, now we just return the object (which is the only thing left on the stack)
emit.Return();
return emit.CreateDelegate();
}
public class Sample
{
public string One { get; set; }
public string Two { get; set; }
public string Three { get; set; }
public string Four { get; set; }
}
@dotnetchris

This comment has been minimized.

Copy link
Owner Author

commented Feb 19, 2016

Forked this to save for myself for later.

Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment
You can’t perform that action at this time.