Click here to Skip to main content
12,297,947 members (45,675 online)
Click here to Skip to main content
Articles » Languages » C# » Reflection » Downloads

Stats

126.9K views
2.6K downloads
141 bookmarked
Posted

HyperDescriptor: Accelerated dynamic property access

, 20 Apr 2007
Provides a vastly accelerate runtime property implementation that can be applied even to closed-source classes
using System;
using Hyper.ComponentModel;
using System.ComponentModel;
using System.Diagnostics;
using System.Security.Permissions;

/* Change history:
 * 20 Apr 2007  Marc Gravell    Improved timing data; control META_CYCLES separately;
 *                              added GC.Collect between tests (since large object count);
 *                              tested without ReflectionPermssion (thank/credit to Josh Smith)
 * 
 * 
 */
namespace HyperPropertyDescriptorSample {
    public sealed class MyEntityNamePropertyDescriptor : ChainingPropertyDescriptor {
        public MyEntityNamePropertyDescriptor(PropertyDescriptor parent) : base(parent) { }
        public override object GetValue(object component) {
            return (string) ((MyEntity)component).Name;
        }
        public override void SetValue(object component, object value) {
            ((MyEntity)component).Name = (string)value;
        }
        public override bool IsReadOnly {
            get { return false; }
        }
        public override bool SupportsChangeEvents {
            get { return true; }
        }
        public override void AddValueChanged(object component, EventHandler handler) {
            ((MyEntity)component).NameChanged += handler;
        }
        public override void RemoveValueChanged(object component, EventHandler handler) {
            ((MyEntity)component).NameChanged -= handler;
        }        
    }
    
    public class MySuperEntity : MyEntity {
        private DateTime when;
        public DateTime When {
            get {
                opCount++;
                return when;
            }
            set {
                opCount++;
                when = value;
            }
        }
    }
    
    public class MyEntity {
        // opCount is a marker of work done; make protected (as an exception)
        // to minimise impact
        protected int opCount;
        private string name;
        private EventHandler _nameChanged;
        public event EventHandler NameChanged {
            add { opCount++; _nameChanged += value; }
            remove{ opCount++; _nameChanged -= value; }
        }
        public string Name {
            get {
                opCount++;
                return name;
            }
            set {
                opCount++;
                if (value != Name) {
                    name = value;
                    EventHandler handler = _nameChanged;
                    if (handler != null) handler(this, EventArgs.Empty);
                }
            }
        }
        public int OpCount { get { return opCount; } }
    }
    static class Program {
        /// <summary>
        /// The number of standard operations (GetValue(), etc) to perform
        /// </summary>
        const int CYCLES = 5000000;
        /// <summary>
        /// The number of times to invoke GetProperties (slower)
        /// </summary>
        const int META_CYCLES = 100000;

        static void Main() {
            // verify that things work OK without reflection access
            new ReflectionPermission(ReflectionPermissionFlag.AllFlags).Deny();

            Console.WriteLine("Direct access");
            TestDirect(1, false); // for JIT etc
            TestDirect(CYCLES, true);

            Console.WriteLine();
            string typeName = typeof(HyperTypeDescriptionProvider).Name;
            Console.WriteLine("Without " + typeName);

            RunTests(1, 1, false); // for JIT etc
            RunTests(META_CYCLES, CYCLES, true);
            
            HyperTypeDescriptionProvider.Add(typeof(MyEntity));
            Console.WriteLine();
            Console.WriteLine("With " + typeName);

            RunTests(1, 1, false); // for Emit, JIT etc
            RunTests(META_CYCLES, CYCLES, true);
        }
        static void RunTests(int metaCount, int count, bool report) {
            // note: GC.Collect here in timing mode (report==true) to
            // minimise chance of increasing object count triggering GC
            // for later tests. Not recommended for production code, but
            // acceptable for levelling the field in performance tests.
            if (report) { GC.Collect(); }
            Test<MyEntity>(metaCount, count, "Name", report);
            if (report) { GC.Collect(); }
            Test<MySuperEntity>(metaCount, count, "Name", report);
            if (report) { GC.Collect(); }
            Test<MySuperEntity>(metaCount, count, "When", report);
            if (report) { GC.Collect(); }
        }
        static void TestDirect(int count, bool output) {
            // initialise
            MyEntity t = new MyEntity();
            string value = "";

            // GetValue
            Stopwatch getValue = new Stopwatch();
            getValue.Start();
            DateTime startGetValue = DateTime.Now;
            for (int i = 0; i < count; i++) {
                value = t.Name;
            }
            getValue.Stop();

            // SetValue
            Stopwatch setValue = new Stopwatch();
            setValue.Start();
            for (int i = 0; i < count; i++) {
                t.Name = value;
            }
            setValue.Stop();

            // ValueChanged
            Stopwatch valueChanged = new Stopwatch();
            valueChanged.Start();
            EventHandler handler = ValueChanged;
            for (int i = 0; i < count; i++) {
                t.NameChanged += handler;
                t.NameChanged -= handler;
            }
            valueChanged.Stop();

            if (output) {
                Report<MyEntity>("Name", "GetValue", getValue);
                Report<MyEntity>("Name", "SetValue", setValue);
                Report<MyEntity>("Name", "ValueChanged", valueChanged);
                Console.WriteLine("OpCount: " + t.OpCount);
            }
        }
        static void Test<T>(int metaCount, int count, string name, bool output) where T : MyEntity, new() {
            // initialise
            T t = new T();
            PropertyDescriptorCollection props = null;
            PropertyDescriptor property = null;
            object value = null;
            bool isReadOnly = true, supportsChangeEvents = false;

            // GetProperties
            Stopwatch getProperties = new Stopwatch();
            getProperties.Start();
            for (int i = 0; i < metaCount; i++) {
                props = TypeDescriptor.GetProperties(t);
            }
            getProperties.Stop();
            if (props != null) property = props[name];
            
            // IsReadOnly
            Stopwatch isReadOnlyWatch = new Stopwatch();
            isReadOnlyWatch.Start();
            for(int i = 0; i < count; i++) {
                isReadOnly = property.IsReadOnly;
            }
            isReadOnlyWatch.Stop();

            // SupportsNotification
            Stopwatch supportsChangeEventsWatch = new Stopwatch();
            supportsChangeEventsWatch.Start();
            for(int i = 0; i < count; i++) {
                supportsChangeEvents = property.SupportsChangeEvents;
            }
            supportsChangeEventsWatch.Stop();

            // GetValue
            Stopwatch getValue = new Stopwatch();
            getValue.Start();
            for(int i = 0; i < count; i++) {
                value = property.GetValue(t);
            }
            getValue.Stop();

            // SetValue
            Stopwatch setValue = new Stopwatch();
            if(!isReadOnly) {
                setValue.Start();
                for(int i = 0; i < count; i++) {
                    property.SetValue(t, value);
                }
                setValue.Stop();
            }
            
            // ValueChanged
            Stopwatch valueChanged = new Stopwatch();
            if(supportsChangeEvents) {
                EventHandler handler = ValueChanged;
                valueChanged.Start();
                for(int i = 0; i < count; i++) {
                    property.AddValueChanged(t, handler);
                    property.RemoveValueChanged(t, handler);
                }
                valueChanged.Stop();
            }
            
            if(output) {
                Report<T>(name, "GetProperties", getProperties);
                Report<T>(name, "IsReadOnly", isReadOnlyWatch);
                Report<T>(name, "SupportsChangeEvents", supportsChangeEventsWatch);
                Report<T>(name, "GetValue", getValue);
                if(!isReadOnly) {
                    Report<T>(name, "SetValue", setValue);
                }
                if(supportsChangeEvents) {
                    Report<T>(name, "ValueChanged", valueChanged);
                }
                Console.WriteLine("OpCount: " + t.OpCount);
            }
        }

        static void Report<T>(string propertyname, string test, Stopwatch watch)
        {
            int ms = (int) Math.Round(watch.Elapsed.TotalMilliseconds);
            Console.WriteLine("{0}.{1}\t{2}\t{3}ms", typeof(T).Name, propertyname, test, ms);
        }
        
        static void ValueChanged(object sender, EventArgs args) {}        
    }
}

By viewing downloads associated with this article you agree to the Terms of Service and the article's licence.

If a file you wish to view isn't highlighted, and is a text file (not binary), please let us know and we'll add colourisation support for it.

License

This article has no explicit license attached to it but may contain usage terms in the article text or the download files themselves. If in doubt please contact the author via the discussion board below.

A list of licenses authors might use can be found here

Share

About the Author

Marc Gravell
Software Developer Stack Exchange
United Kingdom United Kingdom
Geek at Stack Exchange

You may also be interested in...

| Advertise | Privacy | Terms of Use | Mobile
Web01 | 2.8.160525.2 | Last Updated 20 Apr 2007
Article Copyright 2007 by Marc Gravell
Everything else Copyright © CodeProject, 1999-2016
Layout: fixed | fluid