Have you ever get tired of manually call reflection methods to get a value of an hidden field? Yes I do!
In my spare time I found a funny solution to work around this problem.
I found a dummy way to avoid access modifier without losing 'deafult' syntax: thanks to c# dynamic object!
public class MyClass { private int value = 1 ; } private static void Main(string[] args) { MyClass myInstance = new MyClass(); //cannot access private field 'value' here //myInstance.value = 2; dynamic myInstance2 = myInstance.Expose(); //can do it! myInstance2.value = 2; }
To achieve a result like that, I simply created a class inherited by DynamicObject called.. ObjectSpy... sounds good.
This class simply wraps an object, holds a reference to its instance and maps every field, properties, methods of its type. The dirty work is done by c# dynamic runtime and DynamicObject overridable methods.
When I type myInstance2.value, actually dynamic runtime "redirects" the call to its method TryGetMember or TrySetMember.
I just have to do the correct things in those methods: find the correct member, invoke it, return it and so forth...
N.B.
I think if you often use reflection to get or set hidden values, there is something wrong in your code and you should re-design your classes. This post is meant to show capabilities and potential of c# dynamic objects.
The following scripts are not a complete solution, these are only hints I wanted to share.
Anyway I reccomend you to give it a try ;)
class ObjectSpy : DynamicObject { private readonly object _instance; private readonly TypeHelper _typeHelper; //this class gets all the members of a type and provides a search method for every MemberType internal sealed class TypeHelper { readonly MemberInfo[] members; internal TypeHelper(IReflect type) { members = type.GetMembers( BindingFlags.Public | BindingFlags.NonPublic | BindingFlags.Static | BindingFlags.Instance | BindingFlags.GetProperty | BindingFlags.SetProperty); } public FieldInfo GetFieldByName(string name) { return members.OfType<FieldInfo>().FirstOrDefault(f => f.Name == name); } public MemberInfo GetMemberByName(string name) { return members.FirstOrDefault(f => f.Name == name); } public PropertyInfo GetPropertyByName(string name) { return members.OfType<PropertyInfo>().FirstOrDefault(p => p.Name == name); } public MethodInfo GetMethodByNameAndArgs(string name, object[] args) { var argsLength = args == null ? 0 : args.Length; Func<ParameterInfo[],bool> match = parameters => { if (argsLength != parameters.Length) return false; for (int i = 0; i < argsLength; i++) { var parameterType = parameters[i].ParameterType; var input = (args[i] ?? new object()).GetType(); if (!input.IsAssignableFrom(parameterType)) return false; } return true; }; return members.OfType<MethodInfo>().FirstOrDefault(f => f.Name == name && match(f.GetParameters())); } } public ObjectSpy(object instance) { _instance = instance; _typeHelper = new TypeHelper(_instance.GetType()); } private Exception NotSupportedException() { return new NotSupportedException(); } public override bool TrySetMember(SetMemberBinder binder, object value) { var member = _typeHelper.GetMemberByName(binder.Name); if (member != null) { switch (member.MemberType) { case MemberTypes.Field: ((FieldInfo)member).SetValue(_instance, value); return true; case MemberTypes.Property: ((PropertyInfo)member).SetValue(_instance, value); return true; } } throw NotSupportedException(); } public override bool TryGetMember(GetMemberBinder binder, out object result) { var member = _typeHelper.GetMemberByName(binder.Name); if (member != null) { switch (member.MemberType) { case MemberTypes.Field: result = ((FieldInfo)member).GetValue(_instance); return true; case MemberTypes.Property: result = ((PropertyInfo)member).GetValue(_instance); return true; } } throw NotSupportedException(); } public override bool TryInvokeMember(InvokeMemberBinder binder, object[] args, out object result) { var method = _typeHelper.GetMethodByNameAndArgs(binder.Name, args); if (method == null) throw NotSupportedException(); result = method.Invoke(_instance, args); return true; } /* The following methods are 'delegating' instructions to the original object. I'm doing a kind of proxy. */ public override bool TryGetIndex(GetIndexBinder binder, dynamic[] indexes, out object result) { if (indexes.Length > 1) throw NotSupportedException(); result = ((dynamic)_instance)[indexes[0]]; return true; } public override bool TrySetIndex(SetIndexBinder binder, dynamic[] indexes, dynamic value) { if (indexes.Length > 1) throw NotSupportedException(); ((dynamic)_instance)[indexes[0]] = value; return true; } } static class ObjectSpyExtension { // a simple extension method to simplify syntax public static ObjectSpy Expose(this object instance) { return new ObjectSpy(instance); } }
No comments:
Post a Comment