Search code examples
c#dynamicobject

DynamicObject. How execute function through TryInvoke?


I want to execute static functions through DynamicObject, but I don't know how execute saveOperation without specifying the class name typeof(Test1) or typeof(Test2) . How relise this better?

For example

class DynObj : DynamicObject
{
    GetMemberBinder saveOperation;
    public override bool TryGetMember(GetMemberBinder binder, out object result)
    {
        saveOperation = binder;
        result = this;
        return true;
    }

    public override bool TryInvoke(InvokeBinder binder, object[] args, out object result)
    {
        Type myType = typeof(Test1 or Test2 or ....);

        result = myType.GetMethod(saveOperation.Name).Invoke(null, args);
        return true;
    }
 }
 class Program
    {
        static void Main(string[] args)
        {
            dynamic d1 = new DynObj();
            d1.func1(3,6);
            d1.func2(3,6);
        }
  }

 class Test1
 {
    public static void func1(int a, int b){...}
 }

 class Test2
 {
    public static void func2(int a, int b){ ...}
 }

Second way defines static function with attribute ( offered by Carnifex )

 class Test3
 {
    [DynFuncMemberAttribute] 
    public static void func3(int a, int b){...}
 }

and get type

     public override bool TryInvoke(InvokeBinder binder, object[] args, out object result)
    {
        Type myType = null;
        foreach (Type types in Assembly.GetExecutingAssembly().GetTypes())
        {
            foreach (MethodInfo mi in types.GetMethods())
            {
                foreach (CustomAttributeData cad in mi.CustomAttributes)
                {
                    if (cad.AttributeType == typeof(DynFuncMemberAttribute))
                    {
                        myType = types;
                        break;
                    }
                }
            }
        }

        result = (myType != null)? myType.GetMethod(saveOperation.Name).Invoke(null, args): null;
        return myType != null;
    }

Solution

  • You could use some Attributes and set eg. [DynFuncMemberAttribute] to the class or to the method it self.

    Then inside TryInvoke (or constructor) get all types/methods marked with this attribute, build some map/cache and voila :)


    Edit: this is example of use this attribute. Remember that BuildCache() will throw exception if two method with the same name will be found.

    [AttributeUsage(AttributeTargets.Method)]
    class DynFuncMemberAttribute : Attribute
    {
    }
    
    class DynObj : DynamicObject
    {
        Dictionary<string, MethodInfo> cache;
        GetMemberBinder saveOperation;
        public override bool TryGetMember(GetMemberBinder binder, out object result)
        {
            saveOperation = binder;
            result = this;
            return true;
        }
    
        public override bool TryInvoke(InvokeBinder binder, object[] args, out object result)
        {
            if (cache == null)
                cache = BuildCache();
    
            MethodInfo mi;
            if (cache.TryGetValue(saveOperation.Name, out mi))
            {
                result = mi.Invoke(null, args);
                return true;
            }
    
            result = null;
            return false;
        }
    
        private Dictionary<string, MethodInfo> BuildCache()
        {
            return Assembly.GetEntryAssembly()
                .GetTypes()
                .SelectMany(t => t.GetMethods(BindingFlags.Public | BindingFlags.Static))
                .Where(mi => mi.GetCustomAttribute<DynFuncMemberAttribute>() != null)
                .ToDictionary(mi => mi.Name);
        }
    }
    
    class Program
    {
        static void Main(string[] args)
        {
            dynamic d1 = new DynObj();
            d1.func1(3, 6);
            d1.func2(3, 6);
        }
    }
    
    class Test1
    {
        [DynFuncMember]
        public static void func1(int a, int b)
        {
            Console.WriteLine("func1");
        }
    }
    
    class Test2
    {
        [DynFuncMember]
        public static void func2(int a, int b)
        {
            Console.WriteLine("func2");
        }
    }