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