c# - How do I create methods at run time in a pre-existing class? -


i'm using fluent pattern helping unit testing, , result object building. biggest pain point of fluent builder pattern having define these with____ methods each , every property might want set.

when comes object maybe 30 fields might want set, don't want write out 30 methods pretty same thing. i'd rather write out dynamic can handle similar logic me.

for example (psuedo code)

for each property in this.properties    define method( property.name      return type: this.class,     parameter types: [property.type]){      set property property.name, parameters[0]     return   } 

here have far in c#

 var properties = typeof(enginemodelbuilder).getproperties();  foreach (var property in properties){    // how create method here property?    // property has .propertytype , .name    // , return type going 'this'  } 

for reference, here how normal fluent builder method looks:

    public enginemodelbuilder withdescription(string description)     {         _description = description;         return this;     } 

this class came with. utilizes c#'s dynamic object use "method_missing" approach of run-time functionality.

note method_missing ruby, , commonly used kind of dynamic behavior.

dynamicbuilder.cs:

using system; using system.collections.generic; using system.dynamic; using system.linq; using system.reflection; using system.text; using system.threading.tasks;  namespace pgencore {     /// <summary>     /// todo: test different field retreival techniques (using fields t, , managing in dictionary)     ///  - how define defaults?     ///     ///     /// usage:     ///   dynamic objectbuilder = new objectbuilder();     ///   var object = objectbuilder     ///     .withobjectfieldname(appropriatevalue)     ///     .withobjectfield(appropriatevalue2)     ///     .build();     ///     /// </summary>     /// <typeparam name="t">type build</typeparam>     public class dynamicbuilder<t> : dynamicobject     {         #region list of fieldinformation         protected fieldinfo[] fieldinfos;         public fieldinfo[] fieldinformation         {                         {                 // don't getfields time                 if (fieldinfos != null) return fieldinfos;                  fieldinfos = this.gettype().getfields(                     bindingflags.public |                     bindingflags.nonpublic |                     bindingflags.instance);                  return fieldinfos;             }         }         #endregion          #region utility          /// <summary>         /// converts fieldname _fieldname         /// </summary>         /// <param name="publicname"></param>         /// <returns></returns>         public static string publicnametoprivate(string publicname)         {             var propertylowerfirstlettername = char.tolower(                 publicname[0]) + publicname.substring(1);              var privatename = "_" + propertylowerfirstlettername;              return privatename;         }         #endregion          #region method missing? check with{fieldname} pattern          /// <summary>         /// inherited form dynamicobject.         /// ran before each method call.         ///         ///         /// note: works setting 1 value         ///  @ time.         ///   e.g.: instance.object = value         /// </summary>         /// <param name="binder"></param>         /// <param name="args"></param>         /// <param name="result"></param>         /// <returns></returns>         public override bool tryinvokemember(              invokememberbinder binder,              object[] args,              out object result)         {              var firstargument = args[0];             var methodname = binder.name;             var propertyrootname = methodname;              // following builder pattern,             // methods participate in building t,             // return can chain building methods.             result = this;              // booleans, since field / property should named             // question, using "with" doesn't make sense.             // so, logic needed when not setting             // boolean field.             if (!(firstargument bool))             {                 // if not setting bool, , aren't starting                 // "with", method not part of                 // fluent builder pattern.                 if (!methodname.contains("with")) return false;                 propertyrootname = methodname.replace("with", "");             }              // convert _privatefieldname             // todo: think dynamicly having fields in dictionary,             //  rather having specify them             var builderfieldname = publicnametoprivate(propertyrootname);              // find matching field name, given method name             var fieldinfo = fieldinformation                 .firstordefault(                     field => field.name == builderfieldname                  );              // if field not found, abort             if (fieldinfo == null) return false;              // set field value in args             fieldinfo.setvalue(this, firstargument);             return true;         }          #endregion          /// <summary>         /// returns built object         /// </summary>         /// <returns></returns>         public virtual t build()         {             throw new notimplementedexception();         }          /// <summary>         /// complex associations         /// - building lists of items         /// - building else isn't easy vavlue.         /// </summary>         public virtual void setrelationshipdefaults()         {             throw new notimplementedexception();         }     } } 

tests of usage can seen here: https://github.com/nullvoxpopuli/procedural-builder/blob/master/proceduralbuilder.test/dynamicbuildertest.cs


Comments

Popular posts from this blog

python - pip install -U PySide error -

arrays - C++ error: a brace-enclosed initializer is not allowed here before ‘{’ token -

cytoscape.js - How to add nodes to Dagre layout with Cytoscape -