Tuesday, 15 January 2013

c# - Instantiating Immutable Objects With Reflection -



c# - Instantiating Immutable Objects With Reflection -

i created base of operations class help me cut down boilerplate code of initialization of immutable objects in c#,

i'm using lazy initialization in order seek not impact performance lot , wondering how much affecting performance doing this?

this base of operations class:

public class immutableobject<t> { private readonly func<ienumerable<keyvaluepair<string, object>>> initcontainer; protected immutableobject() {} protected immutableobject(ienumerable<keyvaluepair<string,object>> properties) { var fields = gettype().getfields().where(f=> f.ispublic); var fieldsandvalues = fieldinfo in fields bring together keyvaluepair in properties on fieldinfo.name.tolower() equals keyvaluepair.key.tolower() select new {fieldinfo, keyvaluepair.value}; fieldsandvalues.tolist().foreach(fv=> fv.fieldinfo.setvalue(this,fv.value)); } protected immutableobject(func<ienumerable<keyvaluepair<string,object>>> init) { initcontainer = init; } protected t setproperty(string propertyname, object propertyvalue, bool lazy = true) { func<ienumerable<keyvaluepair<string, object>>> mergefunc = delegate { var propertydict = initcontainer == null ? objecttodictonary () : initcontainer(); homecoming propertydict.select(p => p.key == propertyname? new keyvaluepair<string, object>(propertyname, propertyvalue) : p).tolist(); }; var containerconstructor = typeof(t).getconstructors() .first( ce => ce.getparameters().count() == 1 && ce.getparameters()[0].parametertype.name == "func`1"); homecoming (t) (lazy ? containerconstructor.invoke(new[] {mergefunc}) : dictonarytoobject<t>(mergefunc())); } private ienumerable<keyvaluepair<string,object>> objecttodictonary() { var fields = gettype().getfields().where(f=> f.ispublic); homecoming fields.select(f=> new keyvaluepair<string,object>(f.name, f.getvalue(this))).tolist(); } private static object dictonarytoobject<t>(ienumerable<keyvaluepair<string,object>> objectproperties) { var mainconstructor = typeof (t).getconstructors() .first(c => c.getparameters().count()== 1 && c.getparameters().any(p => p.parametertype.name == "ienumerable`1") ); homecoming mainconstructor.invoke(new[]{objectproperties}); } public t toobject() { var properties = initcontainer == null ? objecttodictonary() : initcontainer(); homecoming (t) dictonarytoobject<t>(properties); } }

can implemented so:

public class state:immutableobject<state> { public state(){} public state(ienumerable<keyvaluepair<string,object>> properties):base(properties) {} public state(func<ienumerable<keyvaluepair<string, object>>> func):base(func) {} public readonly int someint; public state someint(int someint) { homecoming setproperty("someint", someint); } public readonly string somestring; public state somestring(string somestring) { homecoming setproperty("somestring", somestring); } }

and can used this:

//creating new empty object var state = new state(); // set fields, homecoming empty object "chained methods". var s2 = state.someint(3).somestring("a string"); // resolves "chained methods" , initialize object setting fields reflection. var s3 = s2.toobject();

as mentioned in comments, create more sense, not "conflate" immutable instance implementation or interface behavior of builder new instances.

you create much cleaner , quite type safe solution way. define marker interfaces , type safe versions thereof:

public interface iimmutable : icloneable { } public interface iimmutablebuilder { } public interface iimmutableof<t> : iimmutable t : class, iimmutable { iimmutablebuilderfor<t> mutate(); } public interface iimmutablebuilderfor<t> : iimmutablebuilder t : class, iimmutable { t source { get; } iimmutablebuilderfor<t> set<tfieldtype>(string fieldname, tfieldtype value); iimmutablebuilderfor<t> set<tfieldtype>(string fieldname, func<t, tfieldtype> valueprovider); iimmutablebuilderfor<t> set<tfieldtype>(expression<func<t, tfieldtype>> fieldexpression, tfieldtype value); iimmutablebuilderfor<t> set<tfieldtype>(expression<func<t, tfieldtype>> fieldexpression, func<tfieldtype, tfieldtype> valueprovider); t build(); }

and provide required basic builder behavior in class below. note error checking/compiled delegate creation omitted sake of brevity/simplicity. cleaner, performance optimized version reasonable level of error checking can found in gist.

public class defaultbuilderfor<t> : iimmutablebuilderfor<t> t : class, iimmutableof<t> { private static readonly idictionary<string, tuple<type, action<t, object>>> _setters; private list<action<t>> _mutations = new list<action<t>>(); static defaultbuilderfor() { _setters = getfieldsetters(); } public defaultbuilderfor(t instance) { source = instance; } public t source { get; private set; } public iimmutablebuilderfor<t> set<tfieldtype>(string fieldname, tfieldtype value) { // notes: error checking omitted & add together if `tfieldtype` not "correct". _mutations.add(inst => _setters[fieldname].item2(inst, value)); homecoming this; } public iimmutablebuilderfor<t> set<tfieldtype>(string fieldname, func<t, tfieldtype> valueprovider) { // notes: error checking omitted & add together if `tfieldtype` not "correct". _mutations.add(inst => _setters[fieldname].item2(inst, valueprovider(inst))); homecoming this; } public iimmutablebuilderfor<t> set<tfieldtype>(expression<func<t, tfieldtype>> fieldexpression, tfieldtype value) { // error checking omitted. var memberexpression = fieldexpression.body memberexpression; homecoming set<tfieldtype>(memberexpression.member.name, value); } public iimmutablebuilderfor<t> set<tfieldtype>(expression<func<t, tfieldtype>> fieldexpression, func<tfieldtype, tfieldtype> valueprovider) { // error checking omitted. var memberexpression = fieldexpression.body memberexpression; var getter = fieldexpression.compile(); homecoming set<tfieldtype>(memberexpression.member.name, inst => valueprovider(getter(inst))); } public t build() { var result = (t)source.clone(); _mutations.foreach(x => x(result)); homecoming result; } private static idictionary<string, tuple<type, action<t, object>>> getfieldsetters() { // note: can optimized using delegate setter creation (il). homecoming typeof(t).getfields(bindingflags.public | bindingflags.instance) .where(x => !x.isliteral) .todictionary( x => x.name, x => setterentry(x.fieldtype, (inst, val) => x.setvalue(inst, val))); } private static tuple<type, action<t, object>> setterentry(type type, action<t, object> setter) { homecoming tuple.create(type, setter); } }

example usage

this used this, using illustration class of state:

public static class illustration { public class state : iimmutableof<state> { public state(int someint, string somestring) { someint = someint; somestring = somestring; } public readonly int someint; public readonly string somestring; public iimmutablebuilderfor<state> mutate() { homecoming new defaultbuilderfor<state>(this); } public object clone() { homecoming base.memberwiseclone(); } public override string tostring() { homecoming string.format("{0}, {1}", someint, somestring); } } public static void run() { var original = new state(10, "initial"); var mutatedinstance = original.mutate() .set("someint", 45) .set(x => x.somestring, "hello so") .build(); console.writeline(mutatedinstance); mutatedinstance = original.mutate() .set(x => x.someint, val => val + 10) .build(); console.writeline(mutatedinstance); } }

with next output:

45, hello 20, initial

c# linq reflection initialization immutability

No comments:

Post a Comment