The Code Slinger

November 17, 2008

Color Transitions in Winform Controls

Filed under: .NET, C#, Tuples, Winform — Pete @ 3:02 pm

If you’ve ever used web-sites like the hosted WordPress blogs (like this one!) and others, you’ve noticed the new sweet hotness is to have screens give feedback on success or failure of an operation by showing color gradient transitions.  The most common is simply “Green to White” (success!) or “Red to Pink” (failure!).  This is a quick way to let the user know they can move along and do more work or whether they had an issue.  Granted, more detailed logging and/or messages are needed on the “fail” side of things, however since code slingers like us write perfect code all the time….we are just gonna go with quick feedback for now ;-)

So, being as I’ve only started in the last few months writing winform/WPF apps, I thought there must be an easy way to do these gradients on forms and other controls quite easily.  No so.  Sure, there are ForeColor and BackColor properties, however transitioning in a gradient-like fashion from one color to the next isn’t “built in” in any way that I could tell.  NOTE: Yes, I’m well aware of the fact that WPF is going to take over the world.  But for now this is a Winform app, and until such time as WPF becomes the de-facto standard, it will remain as such.  Accordingly, please don’t comment about how WPF can handle this with ease.  I know.

So here’s what I came up with:

        public static void UpdateColor(Control ctl, Color from, Color to)

        {

            int step = 1; //Increase if you want the transition to be faster.

            var fromT = new Tuple<int, int, int>(

                Convert.ToInt32(from.R),

                Convert.ToInt32(from.G),

                Convert.ToInt32(from.B));

            var toT = new Tuple<int, int, int>(

                Convert.ToInt32(to.R),

                Convert.ToInt32(to.G),

                Convert.ToInt32(to.B));

            var curT = new Tuple<int, int, int>(fromT);

            var done = new Tuple<bool, bool, bool>(false, false, false);

 

            while (true)

            {

                //Red

                if (curT.First >= toT.First)

                    done.First = true;

                else

                {

                    var tempval = curT.First < toT.First ? curT.First += step : curT.First -= step;

                    curT.First = tempval > toT.First ? toT.First : tempval;

                }

                //Green

                if (curT.Second >= toT.Second)

                    done.Second = true;

                else

                {

                    var tempval = curT.Second < toT.Second ? curT.Second += step : curT.Second -= step;

                    curT.Second = tempval > toT.Second ? toT.Second : tempval;

                }

                //Blue

                if (curT.Third >= toT.Third)

                    done.Third = true;

                else

                {

                    var tempval = curT.Third < toT.Third ? curT.Third += step : curT.Third -= step;

                    curT.Third = tempval > toT.Third ? toT.Third : tempval;

                }

 

                if (done.First && done.Second && done.Third)

                {

                    ctl.BackColor = to;

                    ctl.Refresh();

                    break;

                }

                ctl.BackColor = Color.FromArgb(curT.First, curT.Second, curT.Third);

                ctl.Refresh();

            }

        }

So, basically I’m just getting the integer values of the RGB color scheme for the from and to colors passed in, looping through adding/subtracting from the start values (from) until I get to the end values (to), refreshing the control along the way.  Not too terribly difficult, but interesting none-the-less and quite a nice little UI feature that doesn’t look like most winform applications.

Also, note I’ve added a step variable that you can tweak to adjust how fast or slow the transition occurs.  It’s a pretty significant jump from say 1 to 3, so if you want more granular timing, you could adjust the int storage to decimal and account for parts of steps (such as += 0.3).  Or you could implement a true timing system that would force the transition to take place within a specific time construct (perhaps the next article on this topic??).  But for my needs currently, this is more than sufficient.

And of course, I’m using my handy little Tuple<T1,T2,T3> class, since tuples (or at least the concept of them) rock.  Hint hint…please make this concept a part of C# 4.0 Anders…puuuleeeeeease?

    public class Tuple<T1,T2,T3>

    {

        public T1 First { get; set;}

        public T2 Second { get; set; }

        public T3 Third { get; set; }

 

        public Tuple(T1 t, T2 k, T3 l)

        {

            First = t;

            Second = k;

            Third = l;

        }

        public Tuple(Tuple<T1,T2,T3> t)

        {

            First = t.First;

            Second = t.Second;

            Third = t.Third;

        }

    }

 

Til next time….keep on slangin!

 

November 12, 2008

Impersonation & IDisposable

Filed under: .NET, C#, IDisposable, Impersonation — Pete @ 11:25 am

I’ve used an impersonation helper class for a while now, basically to encapsulate changing the current identity context while executing certain code.  This worked well, however I always got sick of having to remember to call the “undo()” method of my current implementation once I was done with executing my code under whatever login I was impersonating.

So, I updated the class to implement IDisposable so that I can now utilize this within the using() construct provided in C#.   Here’s the class in it’s entirety:

using System;

using System.Runtime.InteropServices;

using System.Security.Principal;

 

namespace Support.Win32

{

    public class Impersonate : IDisposable

    {

        public const int LOGON32_LOGON_INTERACTIVE = 2;

        public const int LOGON32_PROVIDER_DEFAULT = 0;

 

        WindowsImpersonationContext impersonationContext;

 

        public Impersonate()

        {

            //Default login is used.

            this.impersonateValidUser(“username”, “domain”, “password”);

        }

 

        public Impersonate(string username, string domain, string password)

        {

            this.impersonateValidUser(username, domain, password);

        }

 

        [DllImport("advapi32.dll")]

        private static extern int LogonUserA(String lpszUserName,

            String lpszDomain,

            String lpszPassword,

            int dwLogonType,

            int dwLogonProvider,

            ref IntPtr phToken);

        [DllImport("advapi32.dll", CharSet = CharSet.Auto, SetLastError = true)]

        private static extern int DuplicateToken(IntPtr hToken,

            int impersonationLevel,

            ref IntPtr hNewToken);

 

        [DllImport("advapi32.dll", CharSet = CharSet.Auto, SetLastError = true)]

        private static extern bool RevertToSelf();

 

        [DllImport("kernel32.dll", CharSet = CharSet.Auto)]

        private static extern bool CloseHandle(IntPtr handle);

 

        private bool impersonateValidUser(string userName, string domain, string password)

        {

            WindowsIdentity tempWindowsIdentity;

            IntPtr token = IntPtr.Zero;

            IntPtr tokenDuplicate = IntPtr.Zero;

 

            if (RevertToSelf())

            {

                if (LogonUserA(userName, domain, password, LOGON32_LOGON_INTERACTIVE,

                    LOGON32_PROVIDER_DEFAULT, ref token) != 0)

                {

                    if (DuplicateToken(token, 2, ref tokenDuplicate) != 0)

                    {

                        tempWindowsIdentity = new WindowsIdentity(tokenDuplicate);

                        impersonationContext = tempWindowsIdentity.Impersonate();

                        if (impersonationContext != null)

                        {

                            CloseHandle(token);

                            CloseHandle(tokenDuplicate);

                            return true;

                        }

                    }

                }

            }

            if (token != IntPtr.Zero)

                CloseHandle(token);

            if (tokenDuplicate != IntPtr.Zero)

                CloseHandle(tokenDuplicate);

            return false;

        }

 

        private void undoImpersonation()

        {

            impersonationContext.Undo();

        }

 

        public void Dispose()

        {

            Dispose(true);

            GC.SuppressFinalize(this);

        }

 

        ~Impersonate()

        {

            Dispose(false);

        }

 

        protected virtual void Dispose(bool disposing)

        {

            if(disposing)

            {

                //Dispose of managed resources.

                if(impersonationContext != null)

                {

                    this.undoImpersonation();

                    impersonationContext.Dispose();

                    impersonationContext = null;

                }

            }

        }

    }

}

 

Basically, now my code can use the following construct in order to execute code in an impersonated context:

 

                using(var imp = new Impersonate(“username”, “domain”, “password”))

                {

                    //Code that I want to run under this account.

                }

 
That’s it! Now I can be lazy and not have to worry about everything getting “cleaned up”.  What’s more, is I’ve made private the actual calls to impersonateValidUser expressly so that it forces me to use this with the using construct. 
 
Til next time…..keep on slangin!

December 27, 2007

SOSEX – New .NET Extensions for WinDBG

Filed under: .NET, SOS, WinDBG — Pete @ 3:43 pm

Somehow I missed this until today, but Steve Johnson has a nice write-up on some extensions he’s written for debugging managed code with WinDBG/SOS.

I find it easier to debug logic and performance issues using other tools like AntsProfiler, Perfmon logs/counters and VS.NET for the most part, however I do use WinDBG/SOS for occasional issues that only crop up in production environments (such as those caused by heavy load, multi-user deadlocks, etc). 

There is a bit of a learning curve to WinDBG, however once you realize how much information is contained within it (e.g. within a full user stack dump of the aspnet worker process), it is often quite easy to find exactly where the issue in the original managed source lies.

There are already commands like !gcroot and !dumpheap that one could use to track some of these issues down.  However of keen interest to a limited WinDBG user such as myself are the new !refs and !dlk commands.

!refs

Though not a replacement, the !refs command supplements SOS’s !gcroot command by allowing you to view  the immediate references from and to a given object.

0:000> !refs 0000000080000db8
Objects referenced by 0000000080000db8 (System.Threading.Mutex):
0000000080000ef0         32    Microsoft.Win32.SafeHandles.SafeWaitHandle

Objects referencing 0000000080000db8 (System.Threading.Mutex):
0000000080000e08         72    System.Threading.Mutex+<>c__DisplayClass3
0000000080000e50         64    System.Runtime.CompilerServices.RuntimeHelpers+CleanupCode

The sample output above shows only heap references, but !refs will also list all references from handles, stacks, registers and the freachable queues.

 

!dlk

The !dlk command allows you to easily spot deadlocks in your application if you suspect deadlock to be the cause of an application hang. If !dlk detects deadlock, the output will list the sync blocks that are held as well as the sync blocks for which each thread is waiting, as well as the type, method, IL offset, and, if symbols are available, the source code and line number at which each thread is waiting:

0:010> !dlk
Deadlock detected:
CLR thread 4 holds sync block 00000000024c6970 OBJ:000000007fff0f80[System.String] STRVAL=SYNC1
             waits sync block 00000000024c6928 OBJ:000000007fff0fa8[System.String] STRVAL=SYNC2
CLR thread 5 holds sync block 00000000024c6928 OBJ:000000007fff0fa8[System.String] STRVAL=SYNC2
             waits sync block 00000000024c6970 OBJ:000000007fff0f80[System.String] STRVAL=SYNC1
CLR Thread 4 is waiting at ConsoleTestApp.ConsoleTestApp.MonitorDeadlockThreadProc()+0xa4(IL) [C:\dev\ConsoleTestApp\ConsoleTestApp.cs, line 195]
CLR Thread 5 is waiting at ConsoleTestApp.ConsoleTestApp.MonitorDeadlockThreadProc()+0xa4(IL) [C:\dev\ConsoleTestApp\ConsoleTestApp.cs, line 195]

1 deadlock detected.

As you can see, !dlk makes it dead simple to troubleshoot this common kind of deadlock in managed code. One important caveat holds true for !dlk: it only works for deadlocks on “sync blocks”. Put more simply, it will only spot locks created by Monitor.Enter (which is used by the C# lock keyword). !dlk will not catch deadlock on other types of synchronization objects, such as mutexes and semaphores.

Very cool stuff.  Thanks Steve!

December 6, 2007

Unit Testing: Static Entity Method Parameter Counts

Filed under: .NET, C#, NUnit — Pete @ 7:30 pm

A common issue in our development cycle is that sometimes the DBA’s need to modify the signature of a stored procedure, however somehow the .NET developer(s) don’t get notified and so the corresponding calling wrapper to that stored procedure doesn’t get updated to reflect this new parameter (or removal of an obsolete old one).

So being as there is no compile time checking of parameter counts matching up the method call to the proc, everything compiles fine until someone at runtime actually tries to call the code for that procedure…and if conditions are wrong, it will blow up.

So here is my first attempt at writing a unit test harness that will at least notify me at the automated build level that I have a stored procedure who’s parameter count doesn’t match up with my code.

NOTE: The following was written for an Oracle database back-end, but could be easily modified for SQL server or any other DB.

So here’s a class that encapsulates the concept of an Address record.  I’ve omitted all the properties and actual Address data since it’s not germane to this topic.  However what is germane is the use of a static method called GetAddresses that takes optional parameters by which to look up particular addresses.  This is a wrapper method to call the stored procedure “TLB_ADDRESS_PKG.AddressGet”.

    public class Address

    {

        public static string m_SPGet = “TLB_ADDRESS_PKG.AddressGet”;

        public static List<Address> GetAddresses(int? a_ID, int? a_PersonID, int? a_ContactID, int? a_CompanyID)

        {

            return Singleton<DataProxy<AddressData, Address>>.Instance.ExecuteGetMultiple(

                m_SPGet,

                a_ID,

                a_PersonID,

                a_ContactID,

                a_CompanyID);

        }

    }

Now, our input to this procedure call is the four parameters listed.  So we would expect that our AddressGet SP takes exactly four input parameters.  This is what we want to unit test, in case somewhere down the line during further development, a developer or DBA decides to add or remove one of these parameters from the SP. 

So in my corresponding project for unit tests project I have the following class:

    [TestFixture()]

    public class AddressTests

    {

        [Test()]

        public void AddressesGet_Check_Procedure_Parameter_Count()

        {

            int method_pc = DBTestHelper<Address>.GetProcMethodWrapperParamCount(“GetAddresses”);

            int proc_pc = DBTestHelper<Address>.GetProcParamCount(Address.m_SPGet);

            Assert.AreEqual(method_pc, proc_pc);

 

        }

    }

Fairly straight forward, get the parameter count for the procedure from the DB.  Get the parameter count from the method in code, and compare the two.  Now let’s look at how we get each.

Here’s the helper class I created to do the legwork.

    public static class DBTestHelper<T> where T: class

    {

        private static string connectionString = “(your_DB_conn_string);”;

        private static OracleConnection conn = new OracleConnection(connectionString);

 

        public static int GetProcMethodWrapperParamCount(string methodname)

        {

            Type t = typeof(T);

            MethodInfo mi = t.GetMethod(methodname, System.Reflection.BindingFlags.Static | System.Reflection.BindingFlags.Public);

            ParameterInfo[] parms = mi.GetParameters();

            int cparamcount = parms.Length;

            return cparamcount;

        }

 

        private static string[] ProcedurePattern(string proc)

        {

            string[] spArray = proc.ToUpper().Split(new char[] { ‘.’ });

            return spArray;

        }

        public static int GetProcParamCount(string proc)

        {

            string[] spPattern = ProcedurePattern(proc);

 

            if(conn.State == System.Data.ConnectionState.Closed)

                conn.Open();

 

            string qry = “SELECT COUNT(*)” +

            ” FROM user_objects o, user_arguments a” +

            ” WHERE UPPER(o.object_name) = ‘” + spPattern[0] + “‘” +

            ” AND UPPER(a.object_name) = ‘” + spPattern[1] + “‘” +

            ” AND o.object_id = a.object_id” +

            ” AND a.in_out = ‘IN’” +

            ” AND argument_name IS NOT NULL ORDER BY position”;

 

            OracleCommand cmd = new OracleCommand(qry, conn);

 

            return int.Parse(cmd.ExecuteScalar().ToString());

        }

    }

The GetProcMethodWrapperParamCount takes the method name and looks it up on the generic type we used in the call (Address in this case).  The GetProcParamCount method simply calls a standard inline SQL query against the user_objects and user_arguments views built into Oracle which provide us information on an actual DB object, in this case the sproc AddressGet in the package TLB_ADDRESS_PKG.  Notice that I force the in_out property type to be “IN” since you have to provide an OUT param of type sys_refcursor in order to get data back into .NET.  This allows the count to be accurate with respect to input params only, which is what we’re concerned with.

Anyway, nothing too earth shattering here, but it is a nice clean way to unit test that your retrieval procedure parameter count matches the calling code in .NET that you’ve written to encapsulate it.

In a follow up to this (when I get time), I will show how an additional unit test can tell us further whether or not each parameter in the calling .NET method matches the expected Oracle parameter type, giving us an even clearer way of knowing if our procedure calls will blow up or not.

Til next time….keep on slangin!

December 3, 2007

Dynamic Object Instantiation: Performance (Part II)

Filed under: .NET, C#, MSIL — Pete @ 8:50 pm

After implementing the findings from Part I I got to thinking about how to utilize the same basic concept for creating, caching and calling property delegates.  Properties are basically just wrappers on two separate method calls from the MSIL’s perspective, a getter and a setter.  So much like I was able to speed up object creation from a generic Proxy type class by writing custom IL code to allow the creation and caching of a delegate that effectively called the constructor of the object, I should be able to do the same thing for creating, caching and calling the properties of a particular class in a generic fashion.

As I was creating a demonstration of such, I found that it was quite simple to create the method (get/set) invocation code.  However in doing so I had to hard code the property type (int, string, bool, etc) since the Proxy container class is only concerned with business object types.   To get around this, I created a separate class called PropertyCaller which is concerned only with the type of the class which contains the property and the property type itself. These two pieces of information, along with the information gleaned from the PropertyInfo object (such as the property Name) allow me to identify a delegate call (get or set) uniquely across any class object I pass in.

Some code may help explain this.  Here’s our basic test class, which just has a single property called ID, that happens to be of type int.

        public class TestClassData

        {

            public int ID { get; set; }

        }

Now, if we want to call this property dynamically, the old way (using the PropertyInfo) object would be to use something like:

        private static void CallViaPI(int id)

        {

            //Init

            TestClassData tcd = new TestClassData();

            PropertyInfo pi = typeof(TestClassData).GetProperty(“ID”);

 

            //Call

            pi.SetValue(tcd, id, null);

            int val = (int)pi.GetValue(tcd, null);

        }

This is slow.  On the order of about 400x what the cost would be to call the ID property directly.  So using our newfound knowledge of creating delegates based on DynamicMethod objects, let’s write a couple of helper methods to do just that.   For the sake of brevity, I’m going to post the entire class I created for this purpose here.  I’ll dissect it below.

    public sealed class PropertyCaller<T, K> where T : class

    {

        private static Dictionary<Type, Dictionary<Type, Dictionary<string, GenGetter>>> dGets =

            new Dictionary<Type, Dictionary<Type, Dictionary<string, GenGetter>>>();

        private static Dictionary<Type, Dictionary<Type, Dictionary<string, GenSetter>>> dSets =

            new Dictionary<Type, Dictionary<Type, Dictionary<string, GenSetter>>>();

 

        public delegate void GenSetter(T target, K value);

        public delegate K GenGetter(T target);

 

        private PropertyCaller()

        {

        }

 

        public static GenGetter CreateGetMethod(PropertyInfo pi)

        {

            //Create the locals needed.

            Type classType = typeof(T);

            Type returnType = typeof(K);

            string propertyName = pi.Name;

 

            //Let’s return the cached delegate if we have one.

            if (dGets.ContainsKey(classType))

            {

                if (dGets[classType].ContainsKey(returnType))

                {

                    if (dGets[classType][returnType].ContainsKey(propertyName))

                        return dGets[classType][returnType][propertyName];

                }

            }

 

            //If there is no getter, return nothing

            MethodInfo getMethod = pi.GetGetMethod();

            if (getMethod == null)

                return null;

 

            //Create the dynamic method to wrap the internal get method

            Type[] arguments = new Type[1];

            arguments[0] = typeof(T);

 

            DynamicMethod getter = new DynamicMethod(

                String.Concat(“_Get”, pi.Name, “_”),

                typeof(K),

                new Type[] { typeof(T) },

                pi.DeclaringType);

            ILGenerator gen = getter.GetILGenerator();

            gen.DeclareLocal(typeof(K));

            gen.Emit(OpCodes.Ldarg_0);

            gen.Emit(OpCodes.Castclass, pi.DeclaringType);

            gen.EmitCall(OpCodes.Callvirt, getMethod, null);

            gen.Emit(OpCodes.Ret);

 

            //Create the delegate and return it

            GenGetter genGetter = (GenGetter)getter.CreateDelegate(typeof(GenGetter));

 

            //Cache the delegate for future use.

            Dictionary<Type, Dictionary<string, GenGetter>> tempDict = null;

            Dictionary<string, GenGetter> tempPropDict = null;

            if (!dGets.ContainsKey(classType))

            {

                tempPropDict = new Dictionary<string, GenGetter>();

                tempPropDict.Add(propertyName, genGetter);

                tempDict = new Dictionary<Type, Dictionary<string, GenGetter>>();

                tempDict.Add(returnType, tempPropDict);

                dGets.Add(classType, tempDict);

            }

            else

            {

                if (!dGets[classType].ContainsKey(returnType))

                {

                    tempPropDict = new Dictionary<string, GenGetter>();

                    tempPropDict.Add(propertyName, genGetter);

                    dGets[classType].Add(returnType, tempPropDict);

                }

                else

                {

                    if (!dGets[classType][returnType].ContainsKey(propertyName))

                        dGets[classType][returnType].Add(propertyName, genGetter);

                }

            }

            //Return delegate to the caller.

            return genGetter;

        }

 

        public static GenSetter CreateSetMethod(PropertyInfo pi)

        {

            //Create the locals needed.

            Type classType = typeof(T);

            Type returnType = typeof(K);

            string propertyName = pi.Name;

 

            //Let’s return the cached delegate if we have one.

            if (dSets.ContainsKey(classType))

            {

                if (dSets[classType].ContainsKey(returnType))

                {

                    if (dSets[classType][returnType].ContainsKey(propertyName))

                        return dSets[classType][returnType][propertyName];

                }

            }

 

            //If there is no setter, return nothing

            MethodInfo setMethod = pi.GetSetMethod();

            if (setMethod == null)

                return null;

 

            //Create dynamic method

            Type[] arguments = new Type[2];

            arguments[0] = typeof(T);

            arguments[1] = typeof(K);

 

            DynamicMethod setter = new DynamicMethod(

                String.Concat(“_Set”, pi.Name, “_”),

                typeof(void),

                arguments,

                pi.DeclaringType);

            ILGenerator gen = setter.GetILGenerator();

            gen.Emit(OpCodes.Ldarg_0);

            gen.Emit(OpCodes.Castclass, pi.DeclaringType);

            gen.Emit(OpCodes.Ldarg_1);

 

            if (pi.PropertyType.IsClass)

                gen.Emit(OpCodes.Castclass, pi.PropertyType);

 

            gen.EmitCall(OpCodes.Callvirt, setMethod, null);

            gen.Emit(OpCodes.Ret);

 

            //Create the delegate

            GenSetter genSetter = (GenSetter)setter.CreateDelegate(typeof(GenSetter));

 

            //Cache the delegate for future use.

            Dictionary<Type, Dictionary<string, GenSetter>> tempDict = null;

            Dictionary<string, GenSetter> tempPropDict = null;

            if (!dSets.ContainsKey(classType))

            {

                tempPropDict = new Dictionary<string, GenSetter>();

                tempPropDict.Add(propertyName, genSetter);

                tempDict = new Dictionary<Type, Dictionary<string, GenSetter>>();

                tempDict.Add(returnType, tempPropDict);

                dSets.Add(classType, tempDict);

            }

            else

            {

                if (!dSets[classType].ContainsKey(returnType))

                {

                    tempPropDict = new Dictionary<string, GenSetter>();

                    tempPropDict.Add(propertyName, genSetter);

                    dSets[classType].Add(returnType, tempPropDict);

                }

                else

                {

                    if (!dSets[classType][returnType].ContainsKey(propertyName))

                        dSets[classType][returnType].Add(propertyName, genSetter);

                }

            }

            //Return delegate to the caller.

            return genSetter;

        }

    }

The two main methods are CreateSetMethod and CreateGetMethod.  They basically handle the creation of the proper delegate based on 1) the class type, 2) the property return type and 3) the property name. These 3 pieces of information are critical to creating a delegate which will end up calling the right class’s property.   Walking through the basics of each we see that first we check the internal cache to short circuit if we already have a delegate which matches the information provided.  This internal cache is simply made up of a multi-dimensional dictionary that categorizes the delegates by:

  • Class Type
  • Property return Type
  • Property Name

Next if we don’t have it cached, we need to create the appropriate delegate.  The basics of this consist of 1) creating the method’s parameters, 2) creating a DynamicMethod wrapper, 3) emitting the IL necessary for the method and finally 4) creating the delegate itself.

Finally we need to cache the newly created delegate for future use.  The overhead to create the delegates would make this approach prohibitively slow if we didn’t cache them (on the order of over 60x slower than using the reflective SetValue/GetValue methods!!). 

Here’s a test harness method which would utilize this new dynamic property delegate creation code.  Obviously (as with the reflection method above, in real usage we would split the initizliation code from the actuall usage code, but this should give you a clear view of how simple both are to actually use). 

        private static void CallViaDelegate(int id)

        {

            //Init

            TestClassData tcd = new TestClassData();

            PropertyInfo tpi = typeof(TestClassData).GetProperty(“ID”);

            PropertyCaller<TestClassData, int>.GenGetter getter = PropertyCaller<TestClassData, int>.CreateGetMethod(tpi);

            PropertyCaller<TestClassData, int>.GenSetter setter = PropertyCaller<TestClassData, int>.CreateSetMethod(tpi);

 

            //Call

            setter(tcd, id);

            int val = getter(tcd);

        }

So, how well does it stack up performance you ask?

property_test_full

The delegate method covered is roughly 3X slower than calling the property directly.  Compare that to the old reflection driven approach which is roughly 450X slower and I think you’ll see the power and potential this has!

 

Til next time…keep on slangin!


Example Project Download

Next Page »

Blog at WordPress.com.