Suggestion to Make Exposed Objects Recurse Inheritance Tree

Apr 5, 2011 at 10:53 PM

I was writing a unit test and wanted to call a private method in my base class.  Unfortunately ExposedObjects doesn't support this.  I added the following to add this feature.  

 

private ExposedObject(object obj)
        {
            m_object = obj;
            InitType(obj.GetType());
        }

        private void InitType(Type type)
        {
            m_type = type;

            m_instanceMethods =
                m_type
                    .GetMethods(BindingFlags.NonPublic | BindingFlags.Public | BindingFlags.Instance)
                    .Where(m => !m.IsGenericMethod)
                    .GroupBy(m => m.Name)
                    .ToDictionary(
                        p => p.Key,
                        p => p.GroupBy(r => r.GetParameters().Length).ToDictionary(r => r.Key, r => r.ToList()));

            m_genInstanceMethods =
                m_type
                    .GetMethods(BindingFlags.NonPublic | BindingFlags.Public | BindingFlags.Instance)
                    .Where(m => m.IsGenericMethod)
                    .GroupBy(m => m.Name)
                    .ToDictionary(
                        p => p.Key,
                        p => p.GroupBy(r => r.GetParameters().Length).ToDictionary(r => r.Key, r => r.ToList()));
        }
public override bool TryInvokeMember(InvokeMemberBinder binder, object[] args, out object result)
        {
            // Get type args of the call
            Type[] typeArgs = ExposedObjectHelper.GetTypeArgs(binder);
            if (typeArgs != null && typeArgs.Length == 0) typeArgs = null;

            //
            // Try to call a non-generic instance method
            //
            if (typeArgs == null
                    && m_instanceMethods.ContainsKey(binder.Name)
                    && m_instanceMethods[binder.Name].ContainsKey(args.Length)
                    && ExposedObjectHelper.InvokeBestMethod(args, m_objectm_instanceMethods[binder.Name][args.Length], out result))
            {
                return true;
            }

            //
            // Try to call a generic instance method
            //
            if (m_instanceMethods.ContainsKey(binder.Name)
                    && m_instanceMethods[binder.Name].ContainsKey(args.Length))
            {
                List<MethodInfo> methods = new List<MethodInfo>();

                foreach (var method in m_genInstanceMethods[binder.Name][args.Length])
                {
                    if (method.GetGenericArguments().Length == typeArgs.Length)
                    {
                        methods.Add(method.MakeGenericMethod(typeArgs));
                    }
                }

                if (ExposedObjectHelper.InvokeBestMethod(args, m_object, methods, out result))
                {
                    return true;
                }
            }
            if (m_type.BaseType != null)
            {
                InitType(m_type.BaseType);
                return TryInvokeMember(binder, args, out result);        
            }

            result = null;
            return false;
        }
Nov 1, 2013 at 7:20 PM
First off this ExposedObject class is really helpful for testing private / proteced methods. I am surprised it hasn't been downloaded more. I would like to add to the code above. The code doesn't return to the top class. It will stay in the base class when it looks for a method. The code below fixes this. The only thing the code below doesn't do it is not able to access a bse private / protected property.

using System;
using System.Collections.Generic;
using System.Dynamic;
using System.Linq;
using System.Reflection;

namespace IgorO.ExposedObjectProject
{
public class ExposedObject : DynamicObject
{
    private object m_object;
    private Type m_type;
    private Type m_originalType;
    private Dictionary<string, Dictionary<int, List<MethodInfo>>> m_instanceMethods;
    private Dictionary<string, Dictionary<int, List<MethodInfo>>> m_genInstanceMethods;

    private ExposedObject(object obj)
    {
        m_object = obj;
        m_type = obj.GetType();
        m_originalType = m_type;
        InitType(obj.GetType());
    }

    public static T Cast<T>(object o)
    {
        return (T)o;
    }

    private void InitType(Type type)
    {
        m_type = type;
        m_instanceMethods = m_type.GetMethods(BindingFlags.NonPublic | BindingFlags.Public | BindingFlags.Instance)
            .Where(m => !m.IsGenericMethod)
            .GroupBy(m => m.Name)
            .ToDictionary(p => p.Key, p => p.GroupBy(r => r.GetParameters().Length)
            .ToDictionary(r => r.Key, r => r.ToList()));

        m_genInstanceMethods = m_type.GetMethods(BindingFlags.NonPublic | BindingFlags.Public | BindingFlags.Instance)
            .Where(m => m.IsGenericMethod)
            .GroupBy(m => m.Name)
            .ToDictionary(p => p.Key, p => p.GroupBy(r => r.GetParameters().Length)
            .ToDictionary(r => r.Key, r => r.ToList()));
    }

    public object Object { get { return m_object; } }

    public static dynamic New<T>()
    {
        return New(typeof(T));
    }

    public static dynamic New(Type type)
    {
        return new ExposedObject(Create(type));
    }

    private static object Create(Type type)
    {
        ConstructorInfo constructorInfo = GetConstructorInfo(type);
        return constructorInfo.Invoke(new object[0]);
    }

    private static ConstructorInfo GetConstructorInfo(Type type, params Type[] args)
    {
        ConstructorInfo constructorInfo = type.GetConstructor(BindingFlags.NonPublic | BindingFlags.Public | BindingFlags.Instance, null, args, null);
        if (constructorInfo != null)
        {
            return constructorInfo;
        }

        throw new MissingMemberException(type.FullName, string.Format(".ctor({0})", string.Join(", ", Array.ConvertAll(args, t => t.FullName))));
    }

    public static dynamic From(object obj)
    {
        return new ExposedObject(obj);
    }

    public static T Cast<T>(ExposedObject t)
    {
        return (T)t.m_object;
    }

    public override bool TryInvokeMember(InvokeMemberBinder binder, object[] args, out object result)
    {
        // Get type args of the call
        Type[] typeArgs = ExposedObjectHelper.GetTypeArgs(binder);
        if (typeArgs != null && typeArgs.Length == 0) typeArgs = null;

        //
        // Try to call a non-generic instance method
        //
        if (typeArgs == null
                && m_instanceMethods.ContainsKey(binder.Name)
                && m_instanceMethods[binder.Name].ContainsKey(args.Length)
                && ExposedObjectHelper.InvokeBestMethod(args, m_object, m_instanceMethods[binder.Name][args.Length], out result))
        {
            return true;
        }

        //
        // Try to call a generic instance method
        //
        if (m_instanceMethods.ContainsKey(binder.Name)
                && m_instanceMethods[binder.Name].ContainsKey(args.Length))
        {
            List<MethodInfo> methods = new List<MethodInfo>();

            foreach (var method in m_genInstanceMethods[binder.Name][args.Length])
            {
                if (method.GetGenericArguments().Length == typeArgs.Length)
                {
                    methods.Add(method.MakeGenericMethod(typeArgs));
                }
            }

            if (ExposedObjectHelper.InvokeBestMethod(args, m_object, methods, out result))
            {
                return true;
            }
        }

        // TEST CODE!!!!

        if (m_type.BaseType != null)
        {
            InitType(m_type.BaseType);
            bool b = TryInvokeMember(binder, args, out result);
            m_type = m_originalType;
            InitType(m_type);

            return b;
        }

        // END TEST CODE!!!!!

        result = null;
        return false;
    }

    public override bool TrySetMember(SetMemberBinder binder, object value)
    {
        var propertyInfo = m_type.GetProperty(
            binder.Name,
            BindingFlags.NonPublic | BindingFlags.Public | BindingFlags.Instance);

        if (propertyInfo != null)
        {
            propertyInfo.SetValue(m_object, value, null);
            return true;
        }

        var fieldInfo = m_type.GetField(
            binder.Name,
            BindingFlags.NonPublic | BindingFlags.Public | BindingFlags.Instance);

        if (fieldInfo != null)
        {
            fieldInfo.SetValue(m_object, value);
            return true;
        }

        return false;
    }

    public override bool TryGetMember(GetMemberBinder binder, out object result)
    {

        var propertyInfo = m_object.GetType().GetProperty(
            binder.Name,
            BindingFlags.NonPublic | BindingFlags.Public | BindingFlags.Instance);

        if (propertyInfo != null)
        {
            result = propertyInfo.GetValue(m_object, null);
            return true;
        }

        var fieldInfo = m_object.GetType().GetField(binder.Name,
            BindingFlags.NonPublic | BindingFlags.Public | BindingFlags.Instance);

        if (fieldInfo != null)
        {
            result = fieldInfo.GetValue(m_object);
            return true;
        }

        if (m_type.BaseType != null)
        {
            InitType(m_type.BaseType);
            bool b = TryGetMember(binder, out result);

            m_type = m_originalType;
            InitType(m_type);

            return b;
        }

        result = null;
        return false;
    }

    public override bool TryConvert(ConvertBinder binder, out object result)
    {
        result = m_object;
        return true;
    }
}
}