/// <summary>
/// C#反射新建类实例和调用类方法及属性
/// </summary>
public class Reflection
{
public static CultureInfo CULTURE_INFO =new CultureInfo("zh-CN",false);
/// <summary>
/// 测试是否为空
/// </summary>
/// <param name="o">The object to test.</param>
/// <returns>True if null was passed as argument.</returns>
public static bool IsNull(object o)
{
return (null == o);
}
/// <summary>
/// Casts a certain valueObject into a certain type, by using ChangeType when possible
/// (i.e. except for exceptions).
/// </summary>
/// <param name="valueObject">The object to cast</param>
/// <param name="typeName">The full name of the type to cast to</param>
/// <returns>A properly casted object, unless an exception occurs.</returns>
public static object CastValue(object valueObject, string typeName)
{
return CastValue(valueObject, Type.GetType(typeName));
}
/// <summary>
/// Casts a certain valueObject into a certain type, by using ChangeType when possible
/// </summary>
/// <param name="valueObject">The object to cast</param>
/// <param name="type">The type to cast to</param>
/// <returns>A properly casted object, unless an exception occurs.</returns>
public static object CastValue(object valueObject, Type type)
{
if (type == typeof(System.Exception))
{
return new System.Exception((string)valueObject);
}
// fix to bug 1474032 provided by Brian Matthews
else if (type.IsEnum)
{
return Enum.Parse(type, valueObject.ToString(), false);
}
else
{
return Convert.ChangeType(valueObject, type, CULTURE_INFO);
}
}
/// <summary>
/// Calls a member on an object, either to get or set an attribute or property,
/// or to invoke a method.
/// </summary>
/// <param name="target">The object on which the member will be called.</param>
/// <param name="name">The name of the member to call.</param>
/// <param name="argValues">An array of arguments. Use null if no argument is needed.</param>
/// <returns>The value returned when the member is a method, else null.</returns>
public static object ObjectCall(object target, string name, object[] argValues)
{
return Call(target.GetType(), target, name, argValues);
}
/// <summary>
/// Calls a static member on a class, either to get or set an attribute or property,
/// or to invoke a method.
/// </summary>
/// <param name="type">The fully qualified name of the class to call.</param>
/// <param name="name">The name of the member to call.</param>
/// <param name="argValues">An array of arguments. Use null if no argument is needed.</param>
/// <returns>The value returned when the member is a method, else null.</returns>
public static object ClassCall(string type, string name, object[] argValues)
{
return Call(GetRuntimeType(type), null, name, argValues);
}
/// <summary>
/// Instantiates a new object.
/// </summary>
/// <param name="type">The fully qualified name of the class to instantiate.</param>
/// <param name="argValues">An array of contructor arguments. Use null if no argument is needed.</param>
/// <returns></returns>
public static object ClassNew(string type, object[] argValues)
{
if (argValues == null) argValues = new object[0];
Type toInstantiate = GetRuntimeType(type);
ConstructorInfo ci = toInstantiate.GetConstructor(GetArgumentTypes(argValues));
if (ci == null)
{
throw new TargetException("No matching constructor found on " + type);
}
return ci.Invoke(argValues);
}
public static object ClassNew(string assembly, string type, object[] argValues)
{
if (argValues == null) argValues = new object[0];
Type toInstantiate = GetRuntimeType(assembly,type);
ConstructorInfo ci = toInstantiate.GetConstructor(GetArgumentTypes(argValues));
if (ci == null)
{
throw new TargetException("No matching constructor found on " + type);
}
return ci.Invoke(argValues);
}
// Private methods ------------------------------------------------------------------
private static object Call(Type type, object target, string name, object[] argValues)
{
int nbOfProvidedArgs = 0;
if (argValues != null) nbOfProvidedArgs = argValues.Length;
// Check if it is a field name
FieldInfo fi = type.GetField(name);
if (fi != null)
{
if (nbOfProvidedArgs > 0)
{
if (nbOfProvidedArgs == 1)
{
fi.SetValue(target, argValues[0]);
return null;
}
else
{
throw new TargetException(nbOfProvidedArgs + " argument(s) provided for field " + target + "." + name + " when 1 expected");
}
}
else
{
return fi.GetValue(target);
}
}
// Check if it is a property name
MethodInfo mi = null;
PropertyInfo pi;
if (nbOfProvidedArgs > 0)
{
// If we have arguments passed, try to locate a property mathing those
pi = type.GetProperty(name, GetArgumentTypes(argValues));
if (pi != null)
{
// If the property is found, give priority to getter
// This is of course a limitation, for very specific things
// NxBRE users will enjoy implementing code delegates!
mi = pi.GetGetMethod();
if (mi == null) mi = pi.GetSetMethod();
}
else
{
// If the property is found, try to find a basic non-argument property
// and target the set method as we have arguments passed
pi = type.GetProperty(name);
if (pi != null) mi = pi.GetSetMethod();
}
}
else
{
// Having no arguments passed, we clearly want a getter.
pi = type.GetProperty(name);
if (pi != null) mi = pi.GetGetMethod();
}
// check if it is a method name
Type[] types = GetArgumentTypes(argValues);
if (mi == null) mi = type.GetMethod(name, types);
// the last option is to try to find if there is a single method with the desired name
if (mi == null)
{
try
{
mi = type.GetMethod(name);
}
catch (AmbiguousMatchException)
{
// IGNORED
}
}
// if something has been found
if (mi != null)
{
if (argValues == null && mi.GetParameters().Length == 0)
return mi.Invoke(target, argValues);
// if the number of arguments match, perform the invocation
else if (argValues.Length == mi.GetParameters().Length)
return mi.Invoke(target, argValues);
}
// nothing found
return CallMethodWithByRefParametersOrThrow(type,
target,
name,
argValues,
new TargetException("Can not find member " + type.FullName
+ "." + name
+ CollectionHelper.ArrayToString(types)
+ " for values "
+ CollectionHelper.ArrayToString(argValues)));
}
private static object CallMethodWithByRefParametersOrThrow(Type type, object target, string methodName, object[] args, TargetException te)
{
MethodInfo[] mis = type.GetMethods();
foreach (MethodInfo mi in mis)
{
ParameterInfo[] parameters = mi.GetParameters();
if ((mi.Name.Equals(methodName)) && (parameters.Length == args.Length))
{
bool allMatch = true;
IList<int> byRefArgIndices = new List<int>();
for (int i = 0; i < parameters.Length; i++)
{
Type rawParameterType = parameters[i].ParameterType;
Type parameterType = rawParameterType.IsByRef ? rawParameterType.GetElementType() : rawParameterType;
if (!(parameterType.IsAssignableFrom(args[i].GetType())))
{
allMatch = false;
break;
}
else if (rawParameterType.IsByRef)
{
byRefArgIndices.Add(i);
}
}
if (allMatch)
{
object invocationResult = mi.Invoke(target, args);
if (byRefArgIndices.Count == 0)
{
return invocationResult;
}
else
{
object[] result = new object[1 + byRefArgIndices.Count];
result[0] = invocationResult;
for (int i = 0; i < byRefArgIndices.Count; i++)
{
result[1 + i] = args[byRefArgIndices[i]];
}
return result;
}
}
}
}
throw te;
}
private static Assembly GetAssembly(string assemblyName)
{
System.Reflection.Assembly assembly = null;
foreach (System.Reflection.Assembly ase in AppDomain.CurrentDomain.GetAssemblies())
{
if (ase.FullName.Trim().Equals(assemblyName.Trim()))
assembly = ase;
}
return assembly;
}
private static Type GetRuntimeType(string type)
{
Type runtimeType = Type.GetType(type, true);
if (runtimeType == null)
{
throw new TargetException("Can not find class type " + type);
}
else
{
return runtimeType;
}
}
public static Type GetRuntimeType(string assemblyName,string type)
{
Assembly assembly = GetAssembly(assemblyName);
if(assembly ==null)
throw new TargetException("Can not find class type " + type);
Type runtimeType = assembly.GetType(type, true);
if (runtimeType == null)
{
throw new TargetException("Can not find class type " + type);
}
else
{
return runtimeType;
}
}
private static Type[] GetArgumentTypes(object[] arguments)
{
ArrayList argumentTypes = new ArrayList();
if(arguments!=null)
for (int i = 0; i < arguments.Length; i++)
{
if (arguments[i] != null)
{
argumentTypes.Add(arguments[i].GetType());
}
else
{
argumentTypes.Add(typeof(Object));
}
}
return (Type[])argumentTypes.ToArray(typeof(Type));
}
}
/// <summary>
/// 获取或设置对象属性中字段值
/// </summary>
public class Munger
{
public Munger()
{
}
public Munger(String aspectName)
{
this.AspectName = aspectName;
}
/// <summary>
/// The name of the aspect that is to be peeked or poked.
/// </summary>
/// <remarks>
/// <para>
/// This name can be a field, property or parameter-less method.
/// </para>
/// <para>
/// The name can be dotted, which chains references. If any link in the chain returns
/// null, the entire chain is considered to return null.
/// </para>
/// </remarks>
/// <example>"DateOfBirth"</example>
/// <example>"Owner.HomeAddress.Postcode"</example>
public string AspectName
{
get { return aspectName; }
set
{
aspectName = value;
if (String.IsNullOrEmpty(aspectName))
this.aspectNameParts = new List<string>();
else
this.aspectNameParts = new List<string>(aspectName.Split('.'));
}
}
private string aspectName;
private List<String> aspectNameParts = new List<string>();
/// <summary>
/// Extract the value indicated by our AspectName from the given target.
/// </summary>
/// <param name="target">The object that will be peeked</param>
/// <returns>The value read from the target</returns>
public Object GetValue(Object target)
{
if (this.aspectNameParts.Count == 0)
return null;
// TODO: refactor this code with the same code that exists in SetValue()
const BindingFlags flags = BindingFlags.Public | BindingFlags.NonPublic | BindingFlags.Instance |
BindingFlags.InvokeMethod | BindingFlags.GetProperty | BindingFlags.GetField;
const BindingFlags flags2 = BindingFlags.Public | BindingFlags.Instance | BindingFlags.GetProperty;
foreach (String property in this.aspectNameParts)
{
if (target == null)
break;
try
{
target = target.GetType().InvokeMember(property, flags, null, target, null);
}
catch (MissingMethodException)
{
// If that didn't work, try to use property as an indexer. This covers things like arrays
// dictionaries and DataRows
try
{
target = target.GetType().InvokeMember("Item", flags2, null, target, new Object[] { property });
}
catch
{
// We could catch MissingMethodException, KeyNotFoundException, TargetInvocationException plus
// others, but basically if anything goes wrong here, we give up
return String.Format("'{0}' is not a parameter-less method, property or field of type '{1}'", property, target.GetType());
}
}
}
return target;
}
/// <summary>
/// Poke the given value into the given target indicated by our AspectName.
/// </summary>
/// <remarks>
/// <para>
/// If the AspectName is a dotted path, all the selectors bar the last
/// are used to find the object that should be updated, and the last
/// selector is used as the property to update on that object.
/// </para>
/// <para>
/// So, if 'target' is a Person and the AspectName is "HomeAddress.Postcode",
/// this method will first fetch "HomeAddress" property, and then try to set the
/// "Postcode" property on the home address object.
/// </para>
/// </remarks>
/// <param name="target">The object that will be poked</param>
/// <param name="value">The value that will be poked into the target</param>
public void PutValue(Object target, Object value)
{
if (this.aspectNameParts.Count == 0)
return;
// Get the object to be poked
const BindingFlags flags = BindingFlags.Public | BindingFlags.NonPublic | BindingFlags.Instance |
BindingFlags.InvokeMethod | BindingFlags.GetProperty | BindingFlags.GetField;
for (int i = 0; i < this.aspectNameParts.Count - 1; i++)
{
if (target == null)
break;
try
{
target = target.GetType().InvokeMember(this.aspectNameParts[i], flags, null, target, null);
}
catch (System.MissingMethodException)
{
// If that didn't work, try to use property as an indexer. This covers things like arrays
// dictionaries and DataRows
try
{
const BindingFlags flags2 = BindingFlags.Public | BindingFlags.Instance | BindingFlags.GetProperty;
target = target.GetType().InvokeMember("Item", flags2, null, target, new Object[] { this.aspectNameParts[i] });
}
catch
{
// We could catch MissingMethodException, KeyNotFoundException, TargetInvocationException plus
// others, but basically if anything goes wrong here, we give up
System.Diagnostics.Debug.WriteLine(String.Format("Cannot invoke '{0}' on a {1}", this.aspectNameParts[i], target.GetType()));
return;
}
}
}
if (target == null)
return;
// Now try to set the value
String lastPart = this.aspectNameParts[this.aspectNameParts.Count - 1];
try
{
// Try to set a property or field first, since that's the most common case
const BindingFlags flags3 = BindingFlags.Public | BindingFlags.NonPublic | BindingFlags.Instance |
BindingFlags.SetProperty | BindingFlags.SetField;
target.GetType().InvokeMember(lastPart, flags3, null, target, new Object[] { value });
}
catch (System.MissingMethodException ex)
{
try
{
// If that failed, it could be method name that we are looking for
const BindingFlags flags4 = BindingFlags.Public | BindingFlags.NonPublic |
BindingFlags.Instance | BindingFlags.InvokeMethod;
target.GetType().InvokeMember(lastPart, flags4, null, target, new Object[] { value });
}
catch (System.MissingMethodException ex2)
{
// If that didn't work, try to use property as an indexer. This covers things like arrays
// dictionaries and DataRows
try
{
const BindingFlags flags5 = BindingFlags.Public | BindingFlags.Instance | BindingFlags.SetProperty;
target = target.GetType().InvokeMember("Item", flags5, null, target,
new Object[] { lastPart, value });
}
catch
{
// We could catch MissingMethodException, KeyNotFoundException, TargetInvocationException plus
// others, but basically if anything goes wrong here, we give up
System.Diagnostics.Debug.WriteLine("Invoke PutAspectByName failed:");
System.Diagnostics.Debug.WriteLine(ex);
System.Diagnostics.Debug.WriteLine(ex2);
}
}
}
}
}