I'm trying to retrieve a value from the following struct.
public struct MyType
{
public string Key { get; set; }
public string Value { get; set; }
}
using the following code.
var mem = new MemberAccessor(typeof(MyType), "Value");
var r = new MyType {Key = "One", Value = "Two"};
object s = mem.Get(r);
Console.WriteLine(s); //Should be Two but is One
And it works, but it gives me the value of "Key" not "Value". If I try to get the value of "Key" then it gives me a "Cannot read from memory" error.
The following is the creation of the Get delegate used in the above code. This only appears to be a problem with a struct.
public class MemberAccessor
{
private readonly Type _targetType;
private readonly Type _memberType;
private readonly MemberInfo _member;
private static readonly Hashtable _mTypeHash = new Hashtable
{
[typeof(sbyte)] = OpCodes.Ldind_I1,
[typeof(byte)] = OpCodes.Ldind_U1,
[typeof(char)] = OpCodes.Ldind_U2,
[typeof(short)] = OpCodes.Ldind_I2,
[typeof(ushort)] = OpCodes.Ldind_U2,
[typeof(int)] = OpCodes.Ldind_I4,
[typeof(uint)] = OpCodes.Ldind_U4,
[typeof(long)] = OpCodes.Ldind_I8,
[typeof(ulong)] = OpCodes.Ldind_I8,
[typeof(bool)] = OpCodes.Ldind_I1,
[typeof(double)] = OpCodes.Ldind_R8,
[typeof(float)] = OpCodes.Ldind_R4
};
public static Type GetMemberInfoType(MemberInfo member)
{
Type type;
if (member is FieldInfo)
type = ((FieldInfo)member).FieldType;
else if (member is PropertyInfo)
type = ((PropertyInfo)member).PropertyType;
else if (member == null)
type = typeof(object);
else
throw new NotSupportedException();
return type;
}
/// <summary>
/// Creates a new property accessor.
/// </summary>
/// <param name="targetType">Target object type.</param>
/// <param name="memberName">Property name.</param>
public MemberAccessor(Type targetType, string memberName)
{
_targetType = targetType;
MemberInfo memberInfo = (targetType).GetProperties().First(x => x.Name == memberName);
if (memberInfo == null)
{
throw new Exception(string.Format("Property \"{0}\" does not exist for type " + "{1}.", memberName, targetType));
}
var canRead = IsField(memberInfo) || ((PropertyInfo)memberInfo).CanRead;
var canWrite = IsField(memberInfo) || ((PropertyInfo)memberInfo).CanWrite;
// roslyn automatically implemented properties, in particular for get-only properties: <{Name}>k__BackingField;
if (!canWrite)
{
var backingFieldName = $"<{memberName}>k__BackingField";
var backingFieldMemberInfo = targetType.GetFields(BindingFlags.Public | BindingFlags.NonPublic | BindingFlags.Instance).FirstOrDefault(x => x.Name == backingFieldName);
if (backingFieldMemberInfo != null)
{
memberInfo = backingFieldMemberInfo;
canWrite = true;
}
}
_memberType = GetMemberInfoType(memberInfo);
_member = memberInfo;
if (canWrite)
{
SetDelegate = GetSetDelegate();
}
if (canRead)
{
GetDelegate = GetGetDelegate();
}
}
private Func<object, object> GetDelegate = null;
private Action<object, object> SetDelegate = null;
/// <summary>
/// Sets the property for the specified target.
/// </summary>
/// <param name="target">Target object.</param>
/// <param name="value">Value to set.</param>
public void Set(object target, object value)
{
SetDelegate?.Invoke(target, value);
}
public object Get(object target)
{
return GetDelegate?.Invoke(target);
}
private Action<object, object> GetSetDelegate()
{
Type[] setParamTypes = new Type[] { typeof(object), typeof(object) };
Type setReturnType = null;
var owner = _targetType.GetTypeInfo().IsAbstract || _targetType.GetTypeInfo().IsInterface ? null : _targetType;
var setMethod = owner != null
? new DynamicMethod(Guid.NewGuid().ToString(), setReturnType, setParamTypes, owner, true)
: new DynamicMethod(Guid.NewGuid().ToString(), setReturnType, setParamTypes, true);
// From the method, get an ILGenerator. This is used to
// emit the IL that we want.
//
ILGenerator setIL = setMethod.GetILGenerator();
//
// Emit the IL.
//
Type paramType = _memberType;
setIL.Emit(OpCodes.Ldarg_0); //Load the first argument
//(target object)
//Cast to the source type
setIL.Emit(OpCodes.Castclass, this._targetType);
setIL.Emit(OpCodes.Ldarg_1); //Load the second argument
//(value object)
if (paramType.GetTypeInfo().IsValueType)
{
setIL.Emit(OpCodes.Unbox, paramType); //Unbox it
if (_mTypeHash[paramType] != null) //and load
{
OpCode load = (OpCode)_mTypeHash[paramType];
setIL.Emit(load);
}
else
{
setIL.Emit(OpCodes.Ldobj, paramType);
}
}
else
{
setIL.Emit(OpCodes.Castclass, paramType); //Cast class
}
if (IsField(_member))
{
setIL.Emit(OpCodes.Stfld, (FieldInfo)_member);
}
else
{
MethodInfo targetSetMethod = GetSetMethodOnDeclaringType(((PropertyInfo)this._member));
if (targetSetMethod != null)
{
setIL.Emit(OpCodes.Callvirt, targetSetMethod);
}
else
{
setIL.ThrowException(typeof(MissingMethodException));
}
}
setIL.Emit(OpCodes.Ret);
var del = setMethod.CreateDelegate(Expression.GetActionType(setParamTypes));
return del as Action<object, object>;
}
public static bool IsField(MemberInfo member)
{
return member is FieldInfo;
}
public static MethodInfo GetSetMethodOnDeclaringType(PropertyInfo propertyInfo)
{
var methodInfo = propertyInfo.GetSetMethod(true);
return methodInfo ?? propertyInfo
.DeclaringType
.GetProperty(propertyInfo.Name)
.GetSetMethod(true);
}
private Func<object, object> GetGetDelegate()
{
Type[] setParamTypes = new[] { typeof(object) };
Type setReturnType = typeof(object);
Type owner = _targetType.GetTypeInfo().IsAbstract || _targetType.GetTypeInfo().IsInterface ? null : _targetType;
var getMethod = owner != null
? new DynamicMethod(Guid.NewGuid().ToString(), setReturnType, setParamTypes, owner, true)
: new DynamicMethod(Guid.NewGuid().ToString(), setReturnType, setParamTypes, true);
// From the method, get an ILGenerator. This is used to
// emit the IL that we want.
ILGenerator getIL = getMethod.GetILGenerator();
getIL.DeclareLocal(typeof(object));
getIL.Emit(OpCodes.Ldarg_0); //Load the first argument
//(target object)
//Cast to the source type
getIL.Emit(OpCodes.Castclass, this._targetType);
//Get the property value
if (IsField(_member))
{
getIL.Emit(OpCodes.Ldfld, (FieldInfo)_member);
if (_memberType.GetTypeInfo().IsValueType)
{
getIL.Emit(OpCodes.Box, _memberType);
}
}
else
{
var targetGetMethod = ((PropertyInfo)_member).GetGetMethod();
getIL.Emit(OpCodes.Callvirt, targetGetMethod);
if (targetGetMethod.ReturnType.GetTypeInfo().IsValueType)
{
getIL.Emit(OpCodes.Box, targetGetMethod.ReturnType);
}
}
getIL.Emit(OpCodes.Ret);
var del = getMethod.CreateDelegate(Expression.GetFuncType(setParamTypes.Concat(new[] { setReturnType }).ToArray()));
return del as Func<object, object>;
}
}
Problem in the IL code that you generate for GetDelegate. You should unbox struct from object type parameter before calling GetMethod of it's property, like this:
if (_targetType.IsValueType)
getIL.Emit(OpCodes.Unbox, _targetType);
else
getIL.Emit(OpCodes.Castclass, this._targetType);
Small side notes about GetGetDelegate method:
For struct types you can use Call instead Callvirt because it's faster and struct can't have virtual properties.
I was't able to understand why do you need declare local variable with this instruction getIL.DeclareLocal(typeof(object)) and I believe you can safely remove this line.
Just in case here version of GetGetDelegate method that worked for me:
private Func<object, object> GetGetDelegate()
{
Type setParamType = typeof(object);
Type[] setParamTypes = { setParamType };
Type setReturnType = typeof(object);
Type owner = _targetType.GetTypeInfo().IsAbstract || _targetType.GetTypeInfo().IsInterface ? null : _targetType;
var getMethod = owner != null
? new DynamicMethod(Guid.NewGuid().ToString(), setReturnType, setParamTypes, owner, true)
: new DynamicMethod(Guid.NewGuid().ToString(), setReturnType, setParamTypes, true);
// From the method, get an ILGenerator. This is used to
// emit the IL that we want.
ILGenerator getIL = getMethod.GetILGenerator();
getIL.Emit(OpCodes.Ldarg_0); //Load the first argument (target object)
if (_targetType.IsValueType)
getIL.Emit(OpCodes.Unbox, _targetType); //unbox struct
else
getIL.Emit(OpCodes.Castclass, this._targetType); //Cast to the source type
Type returnType = null;
if (IsField(_member))
{
getIL.Emit(OpCodes.Ldfld, (FieldInfo)_member);
returnType = _memberType;
}
else
{
var targetGetMethod = ((PropertyInfo)_member).GetGetMethod();
var opCode = _targetType.IsValueType ? OpCodes.Call : OpCodes.Callvirt;
getIL.Emit(opCode, targetGetMethod);
returnType = targetGetMethod.ReturnType;
}
if (returnType.IsValueType)
{
getIL.Emit(OpCodes.Box, returnType);
}
getIL.Emit(OpCodes.Ret);
var del = getMethod.CreateDelegate(Expression.GetFuncType(setParamType, setReturnType));
return del as Func<object, object>;
}
Related
I'm trying to integrate the Transient Fault Handling Application Block in my application via Unity Interception using the VirtualMethodInterceptor.
I created a call handler to create the action or func or task of the intercepted method and pass that into the Transient Fault Handler but now I just get a stackoverflow exception. Which makes sense as the fault handler will call the method which unity will intercept and pass into the fault handler.
What I need is to be able to skip the interception when called back by the Fault Handler or call directly into the base method. Some how unity is able to do this as it will eventually call my classes method.
My interception code
public class RetryHandler : ICallHandler
{
// store a cache of the delegates
private static readonly ConcurrentDictionary<MethodInfo, Func<object, object[], object>> CacheCall =
new ConcurrentDictionary<MethodInfo, Func<object, object[], object>>();
// List of methods we need to call
private static readonly Action<Action> RetryActionMethod = RetryPolicy.NoRetry.ExecuteAction;
private static readonly Func<Func<int>, int> RetryFuncMethod = RetryPolicy.NoRetry.ExecuteAction;
private static readonly Func<Func<Task<int>>, Task<int>> RetryAsyncFuncMethod = RetryPolicy.NoRetry.ExecuteAsync;
private static readonly Func<Func<Task>, Task> RetryAsyncActionMethod = RetryPolicy.NoRetry.ExecuteAsync;
private static readonly ConstructorInfo RetryPolicyConstructor;
static RetryHandler()
{
RetryPolicyConstructor =
typeof (RetryPolicy).GetConstructor(new[]
{typeof (ITransientErrorDetectionStrategy), typeof (RetryStrategy)});
}
public int Order { get; set; }
/// <summary>
/// Uses Expression Trees to wrap method call into retryhandler
/// </summary>
/// <param name="input"></param>
/// <param name="getNext"></param>
/// <returns></returns>
public IMethodReturn Invoke(IMethodInvocation input, GetNextHandlerDelegate getNext)
{
if (input.Arguments.Count == input.Inputs.Count)
{
var methodInfo = input.MethodBase as MethodInfo;
if (methodInfo != null)
{
var func = CacheCall.GetOrAdd(methodInfo, BuildLambda);
var results = func(input.Target, input.Inputs.OfType<object>().ToArray());
var ex = results as Exception;
if (ex != null)
{
return input.CreateExceptionMethodReturn(ex);
}
return input.CreateMethodReturn(results);
}
}
return getNext()(input, getNext);
}
private static Func<object, object[], object> BuildLambda(MethodInfo methodInfo)
{
var retryAttribute = methodInfo.GetCustomAttributes<RetryAttribute>(true).First();
// Convert parameters and source object to be able to call method
var target = Expression.Parameter(typeof (object), "target");
var parameters = Expression.Parameter(typeof (object[]), "parameters");
Expression source;
if (methodInfo.DeclaringType == null)
{
source = target;
}
else
{
source = Expression.Convert(target, methodInfo.DeclaringType);
}
var convertedParams =
methodInfo.GetParameters()
.Select(
(p, i) =>
Expression.Convert(Expression.ArrayIndex(parameters, Expression.Constant(i)),
p.ParameterType)).Cast<Expression>()
.ToArray();
//!!!!! **This line of code causes the stackoverflow as this will go back through interception**
var innerExpression = Expression.Call(source, methodInfo, convertedParams);
// get what type of lambda we need to build and what method we need to call on the retry handler
Type returnType;
MethodInfo retryMethod;
if (methodInfo.ReturnType == typeof (void))
{
returnType = typeof (Action);
retryMethod = RetryActionMethod.Method;
}
else if (methodInfo.ReturnType == typeof (Task))
{
returnType = typeof (Func<Task>);
retryMethod = RetryAsyncActionMethod.Method;
}
else if (methodInfo.ReturnType.IsGenericType &&
methodInfo.ReturnType.GetGenericTypeDefinition() == typeof (Task<>))
{
var genericType = methodInfo.ReturnType.GetGenericArguments()[0];
returnType =
typeof (Func<>).MakeGenericType(
typeof (Task<>).MakeGenericType(genericType));
retryMethod = RetryAsyncFuncMethod.Method.GetGenericMethodDefinition().MakeGenericMethod(genericType);
}
else
{
returnType = typeof (Func<>).MakeGenericType(methodInfo.ReturnType);
retryMethod =
RetryFuncMethod.Method.GetGenericMethodDefinition().MakeGenericMethod(methodInfo.ReturnType);
}
var innerLambda = Expression.Lambda(returnType, innerExpression);
var outerLambda = Expression.Lambda(
typeof (Func<,,>).MakeGenericType(typeof (object), typeof (object[]), returnType),
innerLambda,
target, parameters);
// create the retry handler
var retryPolicy = Expression.New(RetryPolicyConstructor,
Expression.Invoke(retryAttribute.TransientErrorDetectionStrategy),
Expression.Invoke(retryAttribute.RetryStrategy));
var passedInTarget = Expression.Parameter(typeof (object), "wrapperTarget");
var passedInParameters = Expression.Parameter(typeof (object[]), "wrapperParamters");
var retryCall = Expression.Call(retryPolicy, retryMethod,
Expression.Invoke(outerLambda, passedInTarget, passedInParameters));
Expression resultExpression;
if (methodInfo.ReturnType != typeof (void))
{
// convert to object so we can have a standard func<object, object[], object>
resultExpression = Expression.Convert(retryCall, typeof (object));
}
else
{
// if void we will set the return results as null - it's what unity wants and plus we keep our signature
var returnTarget = Expression.Label(typeof (object));
resultExpression = Expression.Block(retryCall, Expression.Label(returnTarget, Expression.Constant(null)));
}
var func =
Expression.Lambda<Func<object, object[], object>>(resultExpression, passedInTarget, passedInParameters)
.Compile();
return func;
}
}
My Attributes
public abstract class RetryAttribute : HandlerAttribute
{
public RetryAttribute()
{
RetryStrategy = () =>
Microsoft.Practices.EnterpriseLibrary.TransientFaultHandling.RetryStrategy.NoRetry;
TransientErrorDetectionStrategy = () => RetryPolicy.NoRetry.ErrorDetectionStrategy;
}
public Expression<Func<RetryStrategy>> RetryStrategy { get; protected set; }
public Expression<Func<ITransientErrorDetectionStrategy>> TransientErrorDetectionStrategy { get; protected set; }
public override ICallHandler CreateHandler(IUnityContainer container)
{
return new RetryHandler();
}
}
public class SqlDatabaseeRetryAttribute : RetryAttribute
{
public SqlDatabaseeRetryAttribute()
{
RetryStrategy =
() => Microsoft.Practices.EnterpriseLibrary.TransientFaultHandling.RetryStrategy.DefaultExponential;
TransientErrorDetectionStrategy = () => new SqlDatabaseTransientErrorDetectionStrategy();
}
}
Ended up using Reflection Emit to call the base method and not the virtual method.
/// <summary>
/// Creates a base. call method
/// Needs to do this so we can skip the unity interception and call
/// </summary>
/// <param name="methodInfo"></param>
/// <returns></returns>
private static Delegate BaseMethodCall(MethodInfo methodInfo)
{
// get parameter types
// include the calling type to make it an open delegate
var paramTypes = new[] { methodInfo.DeclaringType.BaseType}.Concat(
methodInfo.GetParameters().Select(pi => pi.ParameterType)).ToArray();
var baseCall = new DynamicMethod(string.Empty, methodInfo.ReturnType, paramTypes, methodInfo.Module);
var il = baseCall.GetILGenerator();
// add all the parameters into the stack
for (var i = 0; i < paramTypes.Length; i++)
{
il.Emit(OpCodes.Ldarg, i);
}
// call the method but not the virtual method
// this is the key to not have the virtual run
il.EmitCall(OpCodes.Call, methodInfo, null);
il.Emit(OpCodes.Ret);
// get the deletage type call of this method
var delegateType = Expression.GetDelegateType(paramTypes.Concat(new[] { methodInfo.ReturnType}).ToArray());
return baseCall.CreateDelegate(delegateType);
}
And change the line BuildLambda that was causing the issue to
// need to create call to bypass unity interception
var baseDelegate = BaseMethodCall(methodInfo);
var innerExpression = Expression.Invoke(Expression.Constant(baseDelegate), new[] { source }.Concat(convertedParams));
I use custom validation attributes instead of the built in DataAnnotation ones. In the DbContext I run through the entities with reflection and configure the model based on these attributes. Unfortunately the following code doesn't work for integer properties. It is probably because it a cannot find the suitable overload for the "Property" method.
/// <summary>
/// This method should return the
/// property configuration object of the modelbuilder
/// mb.Entity<User>().Property(p=>p.Age)
/// </summary>
private PrimitivePropertyConfiguration GetStringPropertyConfiguration(DbModelBuilder mb, Type entityType, string propertyName)
{
//mb.Entity<User>()
var structuralConfiguration = typeof(DbModelBuilder).GetMethod("Entity").MakeGenericMethod(entityType).Invoke(mb, null);
//p=>p.Age
var param = Expression.Parameter(entityType);
var propertyExpression = Expression.Lambda(Expression.Property(param, propertyName), param);
//.Property()
var propertyMethod = structuralConfiguration.GetType().GetMethod("Property", new[] { propertyExpression.GetType() });
if (propertyMethod != null)
{
var stringpropertyConfiguration =
propertyMethod.Invoke(structuralConfiguration, new[] {propertyExpression}) as
PrimitivePropertyConfiguration;
return stringpropertyConfiguration;
}
else
{
throw new Exception("This should not happen");
}
}
//to test
public class Entity
{
public string StringProperty { get; set; }
public int IntegerProperty { get; set; }
}
var stringPropertyConfig = GetStringPropertyConfiguration(mb, typeof (Entity), "StringProperty");
var intPropertyConfig = GetStringPropertyConfiguration(mb, typeof(Entity), "IntegerProperty");
You need to have your Expression<Func<>> strongly typed to your property return type.
Something like this would work for a property of type int :
private PrimitivePropertyConfiguration GetStructPropertyConfiguration(DbModelBuilder mb, Type entityType, string propertyName, Type propertyType)
{
var structuralConfiguration = typeof(DbModelBuilder).GetMethod("Entity")
.MakeGenericMethod(entityType).Invoke(mb, null);
var param = System.Linq.Expressions.Expression.Parameter(entityType);
var funcType = typeof(Func<,>).MakeGenericType(entityType, propertyType);
var expressionType = typeof(System.Linq.Expressions.Expression);
var lambdaMethod = expressionType.GetMethods(BindingFlags.Static | BindingFlags.Public)
.Where(mi => mi.Name == "Lambda").FirstOrDefault();
var genericLambdaMethod = lambdaMethod.MakeGenericMethod(funcType);
var prop = System.Linq.Expressions.Expression.Property(param, propertyName);
var inv = genericLambdaMethod.Invoke(null, new object[] { prop, new[] { param } });
var propertyMethod = this.GetType().GetMethods(BindingFlags.Public|BindingFlags.Instance)
.Where(mi => mi.Name == "Property").FirstOrDefault();
var propertyMethodGeneric = propertyMethod.MakeGenericMethod(propertyType);
var mapping = propertyMethodGeneric.Invoke(structuralConfiguration , new[]{inv})
as PrimitivePropertyConfiguration;
return mapping;
}
Here is the Context :
I try to code a mapper for converting my DomainModel Objects to ViewModel Ojects dynamically. The problem I get, it's when I try to invoke a method of generic class by reflection I get this error :
System.InvalidOperationException : Late bound operations cannot be performed on types or methods for which ContainsGenericParameters is true.
Can someone help-me to figure out where is the fault ? It would be greatly appreciated
Here is the Code (I tried to simplified it) :
public class MapClass<SourceType, DestinationType>
{
public string Test()
{
return test
}
public void MapClassReflection(SourceType source, ref DestinationType destination)
{
Type sourceType = source.GetType();
Type destinationType = destination.GetType();
foreach (PropertyInfo sourceProperty in sourceType.GetProperties())
{
string destinationPropertyName = LookupForPropertyInDestinationType(sourceProperty.Name, destinationType);
if (destinationPropertyName != null)
{
PropertyInfo destinationProperty = destinationType.GetProperty(destinationPropertyName);
if (destinationProperty.PropertyType == sourceProperty.PropertyType)
{
destinationProperty.SetValue(destination, sourceProperty.GetValue(source, null), null);
}
else
{
Type d1 = typeof(MapClass<,>);
Type[] typeArgs = { destinationProperty.GetType(), sourceType.GetType() };
Type constructed = d1.MakeGenericType(typeArgs);
object o = Activator.CreateInstance(constructed, null);
MethodInfo theMethod = d1.GetMethod("Test");
string toto = (string)theMethod.Invoke(o,null);
}
}
}
}
private string LookupForPropertyInDestinationType(string sourcePropertyName, Type destinationType)
{
foreach (PropertyInfo property in destinationType.GetProperties())
{
if (property.Name == sourcePropertyName)
{
return sourcePropertyName;
}
}
return null;
}
}
You need to call GetMethod on the constructed type constructed, not on the type definition d1.
// ...
Type d1 = typeof(MapClass<,>);
Type[] typeArgs = { destinationProperty.GetType(), sourceType.GetType() };
Type constructed = d1.MakeGenericType(typeArgs);
object o = Activator.CreateInstance(constructed, null);
MethodInfo theMethod = constructed.GetMethod("Test");
string toto = (string)theMethod.Invoke(o, null);
// ...
I want to learn dynamic method and its practical example using c#.
Is there any relation between dynamic method and Reflection?
Please help me.
We are using Dynamic methods for speed up Reflection.
Here is code of our reflection optimizer. it is only 10% slower than direct call and 2000 times faster that reflection call
public class ReflectionEmitPropertyAccessor
{
private readonly bool canRead;
private readonly bool canWrite;
private IPropertyAccessor emittedPropertyAccessor;
private readonly string propertyName;
private readonly Type propertyType;
private readonly Type targetType;
private Dictionary<Type,OpCode> typeOpCodes;
public ReflectionEmitPropertyAccessor(Type targetType, string property)
{
this.targetType = targetType;
propertyName = property;
var propertyInfo = targetType.GetProperty(property);
if (propertyInfo == null)
{
throw new ReflectionOptimizerException(string.Format("Property \"{0}\" is not found in type "+ "{1}.", property, targetType));
}
canRead = propertyInfo.CanRead;
canWrite = propertyInfo.CanWrite;
propertyType = propertyInfo.PropertyType;
}
public bool CanRead
{
get { return canRead; }
}
public bool CanWrite
{
get { return canWrite; }
}
public Type TargetType
{
get { return targetType; }
}
public Type PropertyType
{
get { return propertyType; }
}
#region IPropertyAccessor Members
public object Get(object target)
{
if (canRead)
{
if (emittedPropertyAccessor == null)
{
Init();
}
if (emittedPropertyAccessor != null) return emittedPropertyAccessor.Get(target);
}
else
{
throw new ReflectionOptimizerException(string.Format("У свойства \"{0}\" нет метода get.", propertyName));
}
throw new ReflectionOptimizerException("Fail initialize of " + GetType().FullName);
}
public void Set(object target, object value)
{
if (canWrite)
{
if (emittedPropertyAccessor == null)
{
Init();
}
if (emittedPropertyAccessor != null) emittedPropertyAccessor.Set(target, value);
}
else
{
throw new ReflectionOptimizerException(string.Format("Property \"{0}\" does not have method set.", propertyName));
}
throw new ReflectionOptimizerException("Fail initialize of " + GetType().FullName);
}
#endregion
private void Init()
{
InitTypeOpCodes();
var assembly = EmitAssembly();
emittedPropertyAccessor = assembly.CreateInstance("Property") as IPropertyAccessor;
if (emittedPropertyAccessor == null)
{
throw new ReflectionOptimizerException("Shit happense in PropertyAccessor.");
}
}
private void InitTypeOpCodes()
{
typeOpCodes = new Dictionary<Type, OpCode>
{
{typeof (sbyte), OpCodes.Ldind_I1},
{typeof (byte), OpCodes.Ldind_U1},
{typeof (char), OpCodes.Ldind_U2},
{typeof (short), OpCodes.Ldind_I2},
{typeof (ushort), OpCodes.Ldind_U2},
{typeof (int), OpCodes.Ldind_I4},
{typeof (uint), OpCodes.Ldind_U4},
{typeof (long), OpCodes.Ldind_I8},
{typeof (ulong), OpCodes.Ldind_I8},
{typeof (bool), OpCodes.Ldind_I1},
{typeof (double), OpCodes.Ldind_R8},
{typeof (float), OpCodes.Ldind_R4}
};
}
private Assembly EmitAssembly()
{
var assemblyName = new AssemblyName {Name = "PropertyAccessorAssembly"};
var newAssembly = Thread.GetDomain().DefineDynamicAssembly(assemblyName, AssemblyBuilderAccess.Run);
var newModule = newAssembly.DefineDynamicModule("Module");
var dynamicType = newModule.DefineType("Property", TypeAttributes.Public);
dynamicType.AddInterfaceImplementation(typeof(IPropertyAccessor));
dynamicType.DefineDefaultConstructor(MethodAttributes.Public);
var getParamTypes = new[] { typeof(object) };
var getReturnType = typeof(object);
var getMethod = dynamicType.DefineMethod("Get",
MethodAttributes.Public | MethodAttributes.Virtual,
getReturnType,
getParamTypes);
var getIL = getMethod.GetILGenerator();
var targetGetMethod = targetType.GetMethod("get_" + propertyName);
if (targetGetMethod != null)
{
getIL.DeclareLocal(typeof(object));
getIL.Emit(OpCodes.Ldarg_1); //Load the first argument
getIL.Emit(OpCodes.Castclass, targetType); //Cast to the source type
getIL.EmitCall(OpCodes.Call, targetGetMethod, null); //Get the property value
if (targetGetMethod.ReturnType.IsValueType)
{
getIL.Emit(OpCodes.Box, targetGetMethod.ReturnType); //Box
}
getIL.Emit(OpCodes.Stloc_0); //Store it
getIL.Emit(OpCodes.Ldloc_0);
}
else
{
getIL.ThrowException(typeof(MissingMethodException));
}
getIL.Emit(OpCodes.Ret);
var setParamTypes = new[] { typeof(object), typeof(object) };
const Type setReturnType = null;
var setMethod = dynamicType.DefineMethod("Set",
MethodAttributes.Public | MethodAttributes.Virtual,
setReturnType,
setParamTypes);
var setIL = setMethod.GetILGenerator();
var targetSetMethod = targetType.GetMethod("set_" + propertyName);
if (targetSetMethod != null)
{
Type paramType = targetSetMethod.GetParameters()[0].ParameterType;
setIL.DeclareLocal(paramType);
setIL.Emit(OpCodes.Ldarg_1); //Load the first argument //(target object)
setIL.Emit(OpCodes.Castclass, targetType); //Cast to the source type
setIL.Emit(OpCodes.Ldarg_2); //Load the second argument
//(value object)
if (paramType.IsValueType)
{
setIL.Emit(OpCodes.Unbox, paramType); //Unbox it
if (typeOpCodes.ContainsKey(paramType)) //and load
{
var load = typeOpCodes[paramType];
setIL.Emit(load);
}
else
{
setIL.Emit(OpCodes.Ldobj, paramType);
}
}
else
{
setIL.Emit(OpCodes.Castclass, paramType); //Cast class
}
setIL.EmitCall(OpCodes.Callvirt,targetSetMethod, null); //Set the property value
}
else
{
setIL.ThrowException(typeof(MissingMethodException));
}
setIL.Emit(OpCodes.Ret);
// Load the type
dynamicType.CreateType();
return newAssembly;
}
}
implementation is aggregated from different sources and main is this CodeProject article.
You can create a method via DynamicMethod class.
DynamicMethod squareIt = new DynamicMethod(
"SquareIt",
typeof(long),
methodArgs,
typeof(Example).Module);
ILGenerator il = squareIt.GetILGenerator();
il.Emit(OpCodes.Ldarg_0);
il.Emit(OpCodes.Conv_I8);
il.Emit(OpCodes.Dup);
il.Emit(OpCodes.Mul);
il.Emit(OpCodes.Ret);
Fully commented example on MSDN
I should note that the development using this method is very slow and not very useful.
I've searched throughout but can't find the exact answer to my question. Take for instance the following code:
public class Company
{
private string m_strName;
private Customer m_objCustomer;
public Company()
{
m_strName = "";
m_objCustomer = new Customer();
}
public string Name
{
get { return m_strName; }
set { m_strName = value; }
}
public Customer CustomerInformaion
{
get { return m_objCustomer; }
set { m_objCustomer = value; }
}
}
public class Customer
{
private string m_strName;
private Details m_objDetails;
public Customer()
{
m_strName = "";
m_objDetails = new Details();
}
public string Name
{
get { return m_strName; }
set { m_strName = value; }
}
public Details CustomerDetails
{
get { return m_objDetails; }
set { m_objDetails = value; }
}
}
public class Details
{
private string m_strPhoneNumber;
private string m_strEmailAddress;
public Details()
{
m_strPhoneNumber = "";
m_strEmailAddress = "";
}
public string PhoneNumber
{
get { return m_strPhoneNumber; }
set { m_strPhoneNumber = value; }
}
public string EmailAddress
{
get { return m_strEmailAddress; }
set { m_strEmailAddress = value; }
}
}
Now, I have setup a Form that has many text fields where a user can enter information about a customer at a company. One of those fields is the Email Address text field that has a Tag property set to EmailAddress. I want to be able to look at the Tag of the TextBox and iterate through the entire Company object to find a property with a matching Name and Set its value to the Text property of the TextBox. I can locate the property but setting its value has turned out to be quite difficult. Here's what I have thus far:
foreach (PropertyInfo info in m_objCompany.GetType().GetProperties())
{
if (info.PropertyType != typeof(System.String))
{
foreach (PropertyInfo info2 in info.PropertyType.GetProperties())
{
if (objTextBox.Tag.Equals(info2.Name))
{
if (info2.CanWrite)
{
Object objValue = Convert.ChangeType(objTextBox.Text, info.PropertyType);
info2.SetValue(m_objCompany, objValue, null);
}
}
}
}
}
My issue is that when I run the code I receive an error at ChangeType and/or SetValue. The issue is that the Reflection is stopping at info2 and attempting to set the value to the Type of Details - since it is the parent of the Property EmailAddress.
Any help in determining how to point the SetValue to appropriate property would be helpful and appreciated. As I'm sure you well can guess my class is a GREAT deal larger than the example provided with near 100 properties. Most all are string values which will be entered in manually through TextBox objects. I'm attempting to create one routine that can then be called by all TextBox objects whereby the Tag property of the object could be used to indicate which Property of my class I'm trying to set. From there it is off to XML serialization land.
your innermost line
info2.SetValue(m_objCompany, objValue, null);
is trying to set value of the inner property (info2), on the outer object. The outer object doesn't have an inner object.
What you probably want, is something like this:
public void Bar(object m_objCompany)
{
foreach (PropertyInfo info in m_objCompany.GetType().GetProperties())
{
if (info.PropertyType != typeof(System.String))
{
// Somehow create the outer property
object outerPropertyValue = info.PropertyType.GetConstructor(new Type[] { }).Invoke(new object[] { });
foreach (PropertyInfo info2 in info.PropertyType.GetProperties())
{
if ("blah" == "blah")
{
if (info2.CanWrite)
{
Object innerPropertyValue = Convert.ChangeType("blah", info2.PropertyType);
info2.SetValue(outerPropertyValue, innerPropertyValue, null);
}
}
}
info.SetValue(m_objCompany, outerPropertyValue, null);
}
}
}
When you come across a property that you want to set, you need to create that property (outerPropertyValue), then setup the properties of that property (via innerPropertyValue), then set the outer property on the original object (m_objCompany).
Here is some code I use for Reflection. In this case you would want to call the SetValue method.
Reflector.SetValue(TARGET_OBJECT, "Customer.Details.PhoneNumber", "ValueToSet");
UPDATE: Added the missing ConversionResult struct. Sorry for the omission.
using System;
using System.Collections.Generic;
using System.ComponentModel;
using System.Diagnostics.CodeAnalysis;
using System.Globalization;
using System.Reflection;
namespace YourNamespace
{
public struct ConversionResult
{
public Boolean Success;
public object ConvertedValue;
}
public static class Reflector
{
private static BindingFlags DefaultBindings = BindingFlags.Public | BindingFlags.Static | BindingFlags.Instance | BindingFlags.NonPublic;
#region Public Methods
/// <summary>
/// Execute the "codeToExecute" string on the "source" object
/// </summary>
/// <param name="source">Object the code should be executed against</param>
/// <param name="codeToExecute">Code that should be executed ex. 'Person.Age'</param>
/// <returns>The result of execute codeToExecute on source</returns>
public static object GetValue(object source, String codeToExecute)
{
ReflectorResult reflectorResult = GetReflectorResult(source, codeToExecute, true, false);
if (reflectorResult != null)
{
return reflectorResult.Value;
}
return null;
}
/// <summary>
/// Sets the "source" object to the "value" specified in "codeToExecute"
/// </summary>
/// <param name="source">Object the code should be executed against</param>
/// <param name="codeToExecute">Code that should be executed ex. 'Person.Age'</param>
/// <param name="value">Value to set the source+codeToExecute to.</param>
public static Boolean SetValue(object source, String codeToExecute, object value)
{
return SetValue(source, codeToExecute, value, false);
}
/// <summary>
/// Sets the "source" object to the "value" specified in "codeToExecute"
/// </summary>
/// <param name="source">Object the code should be executed against</param>
/// <param name="codeToExecute">Code that should be executed ex. 'Person.Age'</param>
/// <param name="value">Value to set the source+codeToExecute to.</param>
/// <param name="createIfNotExists">Creates items it cannot find</param>
public static Boolean SetValue(object source, String codeToExecute, object value, Boolean createIfNotExists)
{
Boolean executed = true;
ReflectorResult reflectorResult = GetReflectorResult(source, codeToExecute, false, createIfNotExists);
if (reflectorResult != null)
{
TypeConverter typeConverter = null;
PropertyInfo propertyInfo = reflectorResult.MemberInfo as PropertyInfo;
if (propertyInfo != null)
{
if (propertyInfo.CanWrite)
{
typeConverter = GetTypeConverter(propertyInfo);
ConversionResult conversionResult = ConvertValue(value, propertyInfo.PropertyType, typeConverter);
if (conversionResult.Success)
{
propertyInfo.SetValue(reflectorResult.PreviousValue, conversionResult.ConvertedValue, reflectorResult.MemberInfoParameters);
}
else
{
executed = false;
PentaLogger.LogVerbose("Invalid value: " + value);
}
}
}
else
{
FieldInfo fieldInfo = reflectorResult.MemberInfo as FieldInfo;
if (fieldInfo != null)
{
typeConverter = GetTypeConverter(fieldInfo);
ConversionResult conversionResult = ConvertValue(value, fieldInfo.FieldType, typeConverter);
if (conversionResult.Success)
{
fieldInfo.SetValue(reflectorResult.PreviousValue, conversionResult.ConvertedValue);
}
else
{
executed = false;
PentaLogger.LogVerbose("Invalid value: " + value);
}
}
else
{
// both property and field are invalid
executed = false;
}
}
}
else
{
executed = false;
}
return executed;
}
/// <summary>
/// Sets the "source" object to the "value" specified in "codeToExecute"
/// </summary>
/// <param name="source">Object the code should be executed against</param>
/// <param name="codeToExecute">Code that should be executed ex. 'Person.Age'</param>
/// <param name="value">Value to set the source+codeToExecute to.</param>
public static void RunDynamicCode(object source, String codeToExecute)
{
GetReflectorResult(source, codeToExecute, true, false);
}
/// <summary>
/// Executes the method on the "source" object with the passed parameters
/// </summary>
/// <param name="source">Object the code should be executed against</param>
/// <param name="methodName">Method to call</param>
/// <param name="parameters">Method Parameters</param>
public static object ExecuteMethod(object source, String methodName, object[] parameters)
{
if (parameters == null)
{
parameters = new object[0];
}
MethodInfo[] methodInfos = GetMethods(source, methodName);
foreach (MethodInfo methodInfo in methodInfos)
{
object[] convertedParameters = GetParameters(methodInfo, parameters);
if (convertedParameters != null)
{
return methodInfo.Invoke(source, convertedParameters);
}
}
return null;
}
/// <summary>
/// Executes the method on the "source" object with the passed parameters
/// </summary>
/// <param name="source">Object the code should be executed against</param>
/// <param name="methodName">Method to call</param>
/// <param name="parameter">Method Parameter</param>
public static object ExecuteMethod(object source, String methodName, object parameter)
{
return ExecuteMethod(source, methodName, new object[] { parameter });
}
/// <summary>
/// Executes the method on the "source" object with no parameters
/// </summary>
/// <param name="source">Object the code should be executed against</param>
/// <param name="methodName">Method to call</param>
public static object ExecuteMethod(object source, String methodName)
{
return ExecuteMethod(source, methodName, null);
}
/// <summary>
/// Copies all public properties and fields from source to target
/// </summary>
/// <param name="source"></param>
/// <param name="target"></param>
public static void CopyObject(object source, object target)
{
if (source != null && target != null)
{
Type targetType = target.GetType();
Type sourceType = source.GetType();
PropertyInfo[] properties = sourceType.GetProperties(DefaultBindings);
foreach (PropertyInfo sourceProperty in properties)
{
PropertyInfo targetProperty = targetType.GetProperty(sourceProperty.Name, sourceProperty.PropertyType);
if (targetProperty != null && targetProperty.CanRead && targetProperty.CanWrite)
{
object value = sourceProperty.GetValue(source, null);
targetProperty.SetValue(target, value, null);
}
}
FieldInfo[] fields = sourceType.GetFields(DefaultBindings);
foreach (FieldInfo sourceField in fields)
{
FieldInfo targetField = targetType.GetField(sourceField.Name);
if (targetField != null && targetField.IsPublic)
{
object value = sourceField.GetValue(source);
targetField.SetValue(target, value);
}
}
}
}
/// <summary>
/// Convert the object to the correct type
/// </summary>
/// <param name="value">Value to convert</param>
/// <param name="type">Type to convert to</param>
/// <returns>Converted value</returns>
public static ConversionResult ConvertValue(object value, Type type, TypeConverter typeConverter)
{
ConversionResult conversionResult = new ConversionResult();
conversionResult.Success = false;
if (value != null && type != null)
{
Type objectType = value.GetType();
if (objectType == type)
{
conversionResult.Success = true;
conversionResult.ConvertedValue = value;
}
else
{
// If there is an explicit type converter use it
if (typeConverter != null && typeConverter.CanConvertFrom(objectType))
{
try
{
conversionResult.ConvertedValue = typeConverter.ConvertFrom(value);
conversionResult.Success = true;
}
catch (FormatException) { }
catch (Exception e)
{
if (!(e.InnerException is FormatException))
{
throw;
}
}
}
else
{
try
{
conversionResult.ConvertedValue = Convert.ChangeType(value, type, CultureInfo.CurrentCulture);
conversionResult.Success = true;
}
catch (InvalidCastException) { }
}
}
}
return conversionResult;
}
public static Boolean CanCreateObect(String classPath, Assembly assembly, params object[] parameters)
{
Boolean canCreate = false;
Type type = Type.GetType(classPath);
if (type == null)
{
String pathWithAssembly = classPath + ", " + assembly.FullName;
type = Type.GetType(pathWithAssembly);
}
if (type != null)
{
foreach (ConstructorInfo ci in type.GetConstructors())
{
if (ci.IsPublic)
{
ParameterInfo[] constructorParameters = ci.GetParameters();
if (constructorParameters.Length == parameters.Length)
{
for(Int32 i=0; i<constructorParameters.Length; i++)
{
object parameter = parameters[i];
if(parameter == null)
{
continue;
}
ParameterInfo pi = constructorParameters[i];
if (!pi.ParameterType.IsAssignableFrom(parameter.GetType()))
{
break;
}
}
canCreate = true;
break;
}
}
}
}
return canCreate;
}
public static object CreateObject(String classPath, Assembly assembly, params object[] parameters)
{
Type type = Type.GetType(classPath);
if (type == null)
{
String pathWithAssembly = classPath + ", " + assembly.FullName;
type = Type.GetType(pathWithAssembly);
}
if (type == null)
{
return null;
}
return Activator.CreateInstance(type, parameters);
}
#endregion
#region Private Methods
private static ReflectorResult GetReflectorResult(object source, String codeToExecute, bool getLastValue, bool createIfNotExists)
{
ReflectorResult result = new ReflectorResult(source);
try
{
// Split the code into usable fragments
String[] codeFragments = SplitCodeArray(codeToExecute);
for (Int32 i = 0; i < codeFragments.Length; i++)
{
// if the value is null we cannot go any deeper so don't waste your time
if (result.Value == null)
{
return result;
}
String codeFragment = codeFragments[i];
result.PreviousValue = result.Value;
if (codeFragment.Contains("]"))
{
ProcessArray(result, codeFragment, createIfNotExists);
}
else if (codeFragment.Contains(")"))
{
ProcessMethod(result, codeFragment);
}
else
{
// For set properties we do not need the last value
bool retrieveValue = getLastValue;
if (!retrieveValue)
{
// If this is not the last one in the array, get it anyway
retrieveValue = i + 1 != codeFragments.Length;
}
ProcessProperty(result, codeFragment, retrieveValue);
}
}
}
catch (InvalidCodeFragmentException ex)
{
PentaLogger.LogVerbose("Invalid Property: '" + codeToExecute + "' Invalid Fragment: '" + ex.Message + "'");
}
return result;
}
private static String[] SplitCodeArray(String codeToExecute)
{
List<String> items = new List<String>();
Int32 parenAndbracketCount = 0;
String buffer = "";
foreach (Char c in codeToExecute.ToCharArray())
{
if (c == '.')
{
if (buffer.Length > 0)
{
items.Add(buffer);
buffer = "";
}
continue;
}
else if (c == '[')
{
parenAndbracketCount++;
if (buffer.Length > 0)
{
items.Add(buffer);
}
buffer = c.ToString();
}
else if (c == ']' || c == ')')
{
parenAndbracketCount--;
buffer += c;
if (buffer.Length > 0)
{
items.Add(buffer);
buffer = "";
}
}
else if (Char.IsWhiteSpace(c) || Char.IsControl(c))
{
if (parenAndbracketCount == 0)
{
// Skip it
continue;
}
else
{
buffer += c;
}
}
else if (c == '(')
{
parenAndbracketCount++;
buffer += c;
}
else
{
buffer += c;
}
}
if (buffer.Length > 0)
{
items.Add(buffer);
}
return items.ToArray();
}
private static object[] GetParameters(String codeFragment, MemberInfo memberInfo)
{
String parameters = SplitParametersFromMethod(codeFragment);
if (String.IsNullOrEmpty(parameters))
return new object[0];
object[] parameterArray = parameters.Split(',');
return GetParameters(memberInfo, parameterArray);
}
private static object[] GetParameters(MemberInfo memberInfo, object[] parameterArray)
{
ParameterInfo[] parameterInfo = null;
TypeConverter typeConverter = null;
PropertyInfo propertyInfo = memberInfo as PropertyInfo;
if (propertyInfo != null)
{
parameterInfo = propertyInfo.GetIndexParameters();
typeConverter = GetTypeConverter(parameterInfo[0]);
}
else
{
MethodInfo methodInfo = memberInfo as MethodInfo;
if (methodInfo != null)
{
parameterInfo = methodInfo.GetParameters();
}
}
if (parameterInfo == null)
{
return null;
}
object[] returnParameters = new object[parameterInfo.Length];
for (Int32 i = 0; i < parameterArray.Length; i++)
{
ConversionResult converstionResult = ConvertValue(parameterArray[i], parameterInfo[i].ParameterType, typeConverter);
if (converstionResult.Success)
{
returnParameters[i] = converstionResult.ConvertedValue;
}
else
{
return null;
}
}
return returnParameters;
}
private static TypeConverter GetTypeConverter(MemberInfo memberInfo, Type targetType)
{
object[] typeConverters = memberInfo.GetCustomAttributes(typeof(TypeConverterAttribute), true);
if (typeConverters.Length > 0)
{
TypeConverterAttribute typeConverterAttribute = (TypeConverterAttribute)typeConverters[0];
Type typeFromName = Type.GetType(typeConverterAttribute.ConverterTypeName);
if ((typeFromName != null) && typeof(TypeConverter).IsAssignableFrom(typeFromName))
{
return (TypeConverter)Activator.CreateInstance(typeFromName);
}
}
return TypeDescriptor.GetConverter(targetType);
}
private static TypeConverter GetTypeConverter(PropertyInfo propertyInfo)
{
return GetTypeConverter(propertyInfo, propertyInfo.PropertyType);
}
private static TypeConverter GetTypeConverter(FieldInfo fieldInfo)
{
return GetTypeConverter(fieldInfo, fieldInfo.FieldType);
}
private static TypeConverter GetTypeConverter(ParameterInfo parameterInfo)
{
return GetTypeConverter(parameterInfo.Member, parameterInfo.ParameterType);
}
private static ArrayDefinition GetArrayDefinition(object value, String codeToExecute)
{
// All IList classes have an Item property except for System.Array.
List<MemberInfo> retrieveMemberInfos = new List<MemberInfo>();
foreach (PropertyInfo propertyInfo in value.GetType().GetProperties(DefaultBindings))
{
if (propertyInfo.Name == "Item")
{
retrieveMemberInfos.Add(propertyInfo);
}
}
if (retrieveMemberInfos.Count == 0)
{
// We didn't find any Item properties so this is probably an Array. Use the GetValue method
foreach (MethodInfo methodInfo in value.GetType().GetMethods(DefaultBindings))
{
if (methodInfo.Name == "GetValue")
{
retrieveMemberInfos.Add(methodInfo);
}
}
}
// Some members have overloaded this[] methods. Find the correct method.
foreach (MemberInfo memberInfo in retrieveMemberInfos)
{
object[] parameters = GetParameters(codeToExecute, memberInfo);
if (parameters != null)
{
ArrayDefinition arrayDefinition = new ArrayDefinition();
arrayDefinition.Parameters = parameters;
arrayDefinition.RetrieveMemberInfo = memberInfo;
return arrayDefinition;
}
}
return null;
}
private static void ProcessArray(ReflectorResult result, String codeFragment, Boolean createIfNotExists)
{
Int32 failCount = 0;
ArrayDefinition arrayDefinition = GetArrayDefinition(result.Value, codeFragment);
if (arrayDefinition != null)
{
// If this is anything but System.Array we need to call a Property
PropertyInfo propertyInfo = arrayDefinition.RetrieveMemberInfo as PropertyInfo;
if (propertyInfo != null)
{
SetPropertyInfoValue:
try
{
object value = propertyInfo.GetValue(result.Value, arrayDefinition.Parameters);
result.SetResult(value, propertyInfo, arrayDefinition.Parameters);
}
catch (TargetInvocationException ex)
{
failCount++;
if (ex.InnerException is ArgumentOutOfRangeException && failCount == 1 && createIfNotExists)
{
if (CreateArrayItem(result, arrayDefinition))
{
goto SetPropertyInfoValue;
}
}
// Tried to fix it but failed. Blow up
result.Clear();
throw new InvalidCodeFragmentException(codeFragment);
}
}
else
{
// System.Array has a Method to call
MethodInfo methodInfo = arrayDefinition.RetrieveMemberInfo as MethodInfo;
if (methodInfo != null)
{
try
{
// We can't support dynamically creating array items
object value = methodInfo.Invoke(result.Value, arrayDefinition.Parameters);
result.SetResult(value, methodInfo, arrayDefinition.Parameters);
}
catch (TargetInvocationException)
{
result.Clear();
throw new InvalidCodeFragmentException(codeFragment);
}
}
}
}
else
{
result.Clear();
throw new InvalidCodeFragmentException(codeFragment);
}
}
private static Boolean CreateArrayItem(ReflectorResult result, ArrayDefinition arrayDefinition)
{
Type resultType = result.Value.GetType();
Type containedType = null;
if (resultType.IsArray)
{
containedType = resultType.GetElementType();
}
else
{
containedType = resultType.GetGenericArguments()[0];
}
object newInstance = Activator.CreateInstance(containedType);
if (!resultType.IsArray)
{
MethodInfo[] methods = GetMethods(result.Value, "Insert");
foreach (MethodInfo methodInfo in methods)
{
object[] temp = new object[arrayDefinition.Parameters.Length + 1];
arrayDefinition.Parameters.CopyTo(temp, 0);
temp[arrayDefinition.Parameters.Length] = newInstance;
object[] parameters = GetParameters(methodInfo, temp);
if (parameters != null)
{
methodInfo.Invoke(result.Value, parameters);
return true;
}
}
}
return false;
}
private static void ProcessProperty(ReflectorResult result, String codeFragment, bool retrieveValue)
{
// This is just a regular property
PropertyInfo propertyInfo = result.Value.GetType().GetProperty(codeFragment, DefaultBindings);
if (propertyInfo != null)
{
object value = result.Value;
if (retrieveValue)
{
value = propertyInfo.GetValue(result.Value, null);
result.SetResult(value, propertyInfo, null);
}
result.SetResult(value, propertyInfo, null);
}
else
{
// Maybe it is a field
FieldInfo fieldInfo = result.Value.GetType().GetField(codeFragment, DefaultBindings);
if (fieldInfo != null)
{
object value = result.Value;
if (retrieveValue)
{
value = fieldInfo.GetValue(result.Value);
}
result.SetResult(value, fieldInfo, null);
}
else
{
// This item is missing, log it and set the value to null
result.Clear();
throw new InvalidCodeFragmentException(codeFragment);
}
}
}
private static void ProcessMethod(ReflectorResult result, String codeFragment)
{
// This is just a regular property
String methodName = codeFragment.Substring(0, codeFragment.IndexOf('('));
MethodInfo[] methodInfos = GetMethods(result.Value, methodName);
foreach (MethodInfo methodInfo in methodInfos)
{
object[] parameters = GetParameters(codeFragment, methodInfo);
if (parameters != null)
{
object value = methodInfo.Invoke(result.Value, parameters);
result.SetResult(value, null, null);
break;
}
}
}
private static String SplitParametersFromMethod(String codeFragment)
{
char startCharacter = '[';
char endCharacter = ']';
if (codeFragment.EndsWith(")", StringComparison.CurrentCulture))
{
// This is a function
startCharacter = '(';
endCharacter = ')';
}
Int32 startParam = codeFragment.IndexOf(startCharacter) + 1;
if (startParam < 1)
return null;
Int32 endParam = codeFragment.IndexOf(endCharacter);
if (endParam < 0)
return null;
return codeFragment.Substring(startParam, endParam - startParam).Trim();
}
private static MethodInfo[] GetMethods(object value, String methodName)
{
if (String.IsNullOrEmpty(methodName))
{
throw new ArgumentNullException("methodName");
}
if (value == null)
{
return new MethodInfo[0];
}
List<MethodInfo> methodInfos = new List<MethodInfo>();
foreach (MethodInfo methodInfo in value.GetType().GetMethods(DefaultBindings))
{
if (methodInfo.Name == methodName)
{
methodInfos.Add(methodInfo);
}
}
return methodInfos.ToArray();
}
#endregion
#region Helper Classes
private class ArrayDefinition
{
public MemberInfo RetrieveMemberInfo { get; set; }
public object[] Parameters { get; set; }
}
private class ReflectorResult
{
public ReflectorResult(object startValue)
{
SetResult(startValue, null, null);
}
public MemberInfo MemberInfo { get; private set; }
public object[] MemberInfoParameters { get; private set; }
public object PreviousValue { get; set; }
public object Value { get; private set; }
public void SetResult(object value, MemberInfo memberInfo, object[] memberInfoParameters)
{
Value = value;
MemberInfo = memberInfo;
MemberInfoParameters = memberInfoParameters;
}
public void Clear()
{
MemberInfo = null;
Value = null;
PreviousValue = null;
}
}
[Serializable]
[SuppressMessage("Microsoft.Design", "CA1064:ExceptionsShouldBePublic")]
[SuppressMessage("Microsoft.Design", "CA1032:ImplementStandardExceptionConstructors")]
private class InvalidCodeFragmentException : Exception
{
public InvalidCodeFragmentException(String invalidFragment)
: base(invalidFragment)
{
}
}
#endregion
}
}