Click here to Skip to main content
Click here to Skip to main content

Generic Dynamic Methods Primer

By , 23 Aug 2007
Rate this:
Please Sign up or sign in to vote.

Introduction

Do you want to do something bad? Very, very bad? Something that every decent programming book warns you against? Good. I'll show you howSmile | :) .

Put in another way, I'll show you how to read and modify (without reflection) private variables of another class instance.

And he can't do anything to prevent you from peeping...

Background

Data Abstraction is a very useful concept, and I am in no way going to tell you to drop it. But ... (the way to hell starts with "but", I think) sometimes, you must, absolutely must access this very integer that is stored somewhere deep inside another guy's code. Why? Probably you are implementing custom serialization logic, or object to DB persistence, or pre-condition verification. Or you are just curious...

You can use standard Reflection practices (get/set value methods of FieldInfo).

I'll show you another, probably slightly faster way to accomplish this.

Using the Code

Let's look at a sample class:

//first sample class.
//Note - variables are private
class SampleA 
{
    private int m_value;
    private List<string> m_list;

    public SampleA(int a)
    {
          Value = a;
    }

    public int Value
    {
       get { return m_value; }
       set { m_value = value; }
    }

    public List<string> List
    {
       get { return m_list; }
       set { m_list = value; }
    }
}

Now, how can we access those variables? Take a look:

static void Main(string[] args)
{
    //create first test instance
    SampleA sampleA = new SampleA(17);

    //create get/set accessors that can work on int field "m_value" on any instance
    AccessorBuilder.GetFieldValueUnboundDelegate<int, SampleA> valueGetter = 
    AccessorBuilder.CreateGetter<int, SampleA>("m_value");
    AccessorBuilder.SetFieldValueUnboundDelegate<int, SampleA> valueSetter = 
    AccessorBuilder.CreateSetter<int, SampleA>("m_value");

    //fetch value
    int value = valueGetter(sampleA);

    Console.WriteLine(value);

    //change value
    valueSetter(sampleA, 23);

    Console.WriteLine(sampleA.Value);

    //verify
    value = valueGetter(sampleA);
    Console.WriteLine(value);
}

Accessors

Note how we created two very similar and innocent looking entities - getter and setter for the property. They are unbound - i.e. not related with any particular instance of the class, and you have to pass the actual instance reference for each operation.

You had to know the field type and name to create them - and that's all.

Now, we will use the bound version of the same getter and setter:

SampleB sampleB = new SampleB(55);

//define instance bound get/set accessors for int field of SampleB
AccessorBuilder.GetFieldValueBoundDelegate<int> instanceValueGetterB = 
    AccessorBuilder.CreateGetter<int, SampleB>(sampleB, "m_anotherValue");
AccessorBuilder.SetFieldValueBoundDelegate<int> instanceValueSetterB = 
    AccessorBuilder.CreateSetter<int, SampleB>(sampleB, "m_anotherValue");

//store setters in type safe list
List<AccessorBuilder.SetFieldValueBoundDelegate<int>> uniformSet = 
    new List<AccessorBuilder.SetFieldValueBoundDelegate<int>>();

uniformSet.Add(instanceValueSetterA);
uniformSet.Add(instanceValueSetterB);

//store getters in type safe list
List<AccessorBuilder.GetFieldValueBoundDelegate<int>> uniformGet = 
    new List<AccessorBuilder.GetFieldValueBoundDelegate<int>>();

uniformGet.Add(instanceValueGetterA);
uniformGet.Add(instanceValueGetterB);

//initialize both variables to 43
uniformSet.ForEach(
        delegate(AccessorBuilder.SetFieldValueBoundDelegate<int> item) 
        { 
           item(43); 
        }
          );

//get and print
uniformGet.ForEach(
    delegate(AccessorBuilder.GetFieldValueBoundDelegate<int> item)
     {
       int v = item();
       Console.WriteLine("{0}={1}", item.Target.GetType().Name, v);
     });

So what do you see here?
We create two int getters and setters, each working with a different type. We put them in the same list, and access private values uniformly, just like those classes (SampleA and SampleB) are implementing an imaginary IIntFieldOwner interface. It looks like polymorphic access (it is not exactly the same, because neither class can override/overload this access, but still...).

Note that we are now passing an instance (sampleB) to the CreateSetter/CreateGetter methods, so created delegates are bound to specific object instances.

The same functionality can be easily achieved through regular Reflection techniques (with the help of some generic syntax sugar, like here).
But Reflection.Emit makes things faster (and complicated).

Emit

Emit is old technology, existing from Framework 1.0, and targeted to .NET assembler wizards, knowing how to generate new code online, but not on the high level language like C# using CodeDom - on .NET assembler level. I don't know how many people actually used it, anyway it is pretty hard to build something complex using Emit.

In Framework 2.0, they added a more lightweight version of Emit - dynamic modules, assemblies, etc. It allows you to build and generate classes and methods faster, and use them directly from memory. Moreover, they added a pretty nice feature - the ability to bypass security checks. When Dynamic method is built (without the need to create a module, assembly, etc), you can just pass additional parameters to its constructor - "Type that dynamic method is associated with". This clear specification hides the fact that this dynamic method will have access to all private members of the "Type".

So, how do you create a setter, for example?

static private DynamicMethod createSetterImpl<T, O>(string fieldName)
{
  if (fieldName == null)
    throw new ArgumentNullException("fieldName");

  if (fieldName.Length == 0)
    throw new ArgumentException("Field name must be non-empty string");

  FieldInfo fInfo = typeof(O).GetField(fieldName,
    BindingFlags.NonPublic | BindingFlags.Public | BindingFlags.Instance);

  if (fInfo == null)
    throw new MissingFieldException("Can't obtain field " + fieldName + 
        " from class " + typeof(O).Name);

  if (!typeof(T).IsAssignableFrom(fInfo.FieldType))
    throw new InvalidCastException("Field " + fieldName + " of type " + 
        fInfo.FieldType + " can not be casted to " + typeof(T).FullName);

   //build setter
   DynamicMethod setterDef = new 
    DynamicMethod(typeof(O).Name + "_" + fieldName + "_Setter",
        typeof(void),
        new Type[] { typeof(O), typeof(T) },
        typeof(O));

    ILGenerator setterIL = setterDef.GetILGenerator();
    setterIL.Emit(OpCodes.Ldarg_0); //we expect in top of the stack object instance
    setterIL.Emit(OpCodes.Ldarg_1); //here goes value
    //Sfld means store something that is in the stack head to the field
    setterIL.Emit(OpCodes.Stfld, fInfo); 
    setterIL.Emit(OpCodes.Ret); //note: we have to return, even if method is void

    return setterDef;
}

Ok, so we've got some low level beast - DynamicMethod . How do we create our beautiful object-oriented, type safe, generic delegate? This can be done very easily as follows:

static public SetFieldValueUnboundDelegate<T,O > CreateSetter<T, O>(string fieldName)
{
   SetFieldValueUnboundDelegate<T,O> setter = null;
   DynamicMethod setterDef = createSetterImpl<T, O>(fieldName);

   setter = (SetFieldValueUnboundDelegate<T,O>)setterDef.CreateDelegate
            (typeof(SetFieldValueUnboundDelegate<T,O>));

   return setter;
}

And we are back to the line:

AccessorBuilder.SetFieldValueUnboundDelegate<int, SampleA> valueSetter = 
    AccessorBuilder.CreateSetter<int, SampleA>("m_value");

SampleA sampleA = new SampleA(17);

valueSetter(sampleA, 23);

So, as you can see, almost all work is done by the .NET Framework. We just add some generic - related stuff to make things look prettier (or uglierSmile | :) , it depends...).

Improvements

AccessorBuilder can be optimized to cache field information, to prevent FieldInfo fetch for each request.

Sample Project

It is essentially the same code that you see here, but the full version, that includes all four variations: generation of bound and unbound getters and setters. The solution includes a separate file, AccessorBuilder.cs, where all interesting code resides. An example of usage is in program.cs.

History

  • 23rd August, 2007: Initial post

License

This article, along with any associated source code and files, is licensed under The Code Project Open License (CPOL)

About the Author

Alexander Arlievsky
Architect mPrest Systems
Israel Israel
I am working with various Microsoft technologies since 1994 (remember Windows 3.1 ?Smile | :) ). Language of choice - C++/C#. I am mainly interested in software engineering and "generic solutions", so frequently I find myself designing / implementing frameworks and application skeletons, although sometimes I do various stuff from DB to Kernel Mode device drivers. I also love to understand why things are in the way they are, not just how to use them.
Currently I am Chief Software Architect and CTO of the software outsourcing company mPrest Ltd , Israel.

Comments and Discussions

 
GeneralVery useful! Thanks ! Pinmemberbxb21-Nov-07 22:59 
GeneralI'm glad you decided to upload this to CodeProject. PinmemberOmer Mor7-Sep-07 2:06 
GeneralCalling Private Methods Pinmember_awatts29-Aug-07 1:09 
GeneralRe: Calling Private Methods PinmemberAlexander Arlievsky7-Sep-07 7:05 
GeneralRe: Source Link Pinmemberhuwsimpson23-Aug-07 23:08 
GeneralRe: Source Link PinmemberAlexander Arlievsky24-Aug-07 0:55 
GeneralSource link Pinmemberleppie23-Aug-07 22:08 
GeneralRe: Source link PinmemberAlexander Arlievsky24-Aug-07 0:55 
Fixed
 
==========================================================
Alexander Arlievsky
Chief Software Architect & CTO, mPrest Ltd
alex@mprest.com
"The most valuable tools for debugging are brains"
==========================================================

General General    News News    Suggestion Suggestion    Question Question    Bug Bug    Answer Answer    Joke Joke    Rant Rant    Admin Admin   

Use Ctrl+Left/Right to switch messages, Ctrl+Up/Down to switch threads, Ctrl+Shift+Left/Right to switch pages.

| Advertise | Privacy | Mobile
Web01 | 2.8.140415.2 | Last Updated 23 Aug 2007
Article Copyright 2007 by Alexander Arlievsky
Everything else Copyright © CodeProject, 1999-2014
Terms of Use
Layout: fixed | fluid