Permalink
Switch branches/tags
Nothing to show
Find file Copy path
0674d9d Dec 24, 2017
1 contributor

Users who have contributed to this file

1045 lines (914 sloc) 40.4 KB
#region License
/*
**************************************************************
* Author: Rick Strahl
* © West Wind Technologies, 2008 - 2009
* http://www.west-wind.com/
*
* Created: 09/08/2008
*
* Permission is hereby granted, free of charge, to any person
* obtaining a copy of this software and associated documentation
* files (the "Software"), to deal in the Software without
* restriction, including without limitation the rights to use,
* copy, modify, merge, publish, distribute, sublicense, and/or sell
* copies of the Software, and to permit persons to whom the
* Software is furnished to do so, subject to the following
* conditions:
*
* The above copyright notice and this permission notice shall be
* included in all copies or substantial portions of the Software.
*
* THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND,
* EXPRESS OR IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES
* OF MERCHANTABILITY, FITNESS FOR A PARTICULAR PURPOSE AND
* NONINFRINGEMENT. IN NO EVENT SHALL THE AUTHORS OR COPYRIGHT
* HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER LIABILITY,
* WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING
* FROM, OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR
* OTHER DEALINGS IN THE SOFTWARE.
**************************************************************
*/
#endregion
using System;
using System.Reflection;
using System.Collections;
using System.Globalization;
using System.ComponentModel;
using System.Diagnostics;
using System.Collections.Generic;
using System.IO;
using System.Linq;
using System.Linq.Expressions;
using Westwind.Utilities.Properties;
using System.Runtime.InteropServices;
namespace Westwind.Utilities
{
/// <summary>
/// Collection of Reflection and type conversion related utility functions
/// </summary>
public static class ReflectionUtils
{
/// <summary>
/// Binding Flags constant to be reused for all Reflection access methods.
/// </summary>
public const BindingFlags MemberAccess =
BindingFlags.Public | BindingFlags.NonPublic |
BindingFlags.Static | BindingFlags.Instance | BindingFlags.IgnoreCase;
#region Type and Assembly Creation
/// <summary>
/// Creates an instance from a type by calling the parameterless constructor.
///
/// Note this will not work with COM objects - continue to use the Activator.CreateInstance
/// for COM objects.
/// <seealso>Class wwUtils</seealso>
/// </summary>
/// <param name="typeToCreate">
/// The type from which to create an instance.
/// </param>
/// <returns>object</returns>
public static object CreateInstanceFromType(Type typeToCreate, params object[] args)
{
if (args == null)
{
Type[] Parms = Type.EmptyTypes;
return typeToCreate.GetConstructor(Parms).Invoke(null);
}
return Activator.CreateInstance(typeToCreate, args);
}
/// <summary>
/// Creates an instance of a type based on a string. Assumes that the type's
/// </summary>
/// <param name="typeName"></param>
/// <param name="args"></param>
/// <returns></returns>
public static object CreateInstanceFromString(string typeName, params object[] args)
{
object instance = null;
try
{
var type = GetTypeFromName(typeName);
if (type == null)
return null;
instance = Activator.CreateInstance(type, args);
}
catch
{
return null;
}
return instance;
}
/// <summary>
/// Helper routine that looks up a type name and tries to retrieve the
/// full type reference using GetType() and if not found looking
/// in the actively executing assemblies and optionally loading
/// the specified assembly name.
/// </summary>
/// <param name="typeName">type to load</param>
/// <param name="assemblyName">
/// Optional assembly name to load from if type cannot be loaded initially.
/// Use for lazy loading of assemblies without taking a type dependency.
/// </param>
/// <returns>null</returns>
public static Type GetTypeFromName(string typeName, string assemblyName )
{
var type = Type.GetType(typeName, false);
if (type != null)
return type;
var assemblies = AppDomain.CurrentDomain.GetAssemblies();
// try to find manually
foreach (Assembly asm in assemblies)
{
type = asm.GetType(typeName, false);
if (type != null)
break;
}
if (type != null)
return type;
// see if we can load the assembly
if (!string.IsNullOrEmpty(assemblyName))
{
var a = LoadAssembly(assemblyName);
if (a != null)
{
type = Type.GetType(typeName, false);
if (type != null)
return type;
}
}
return null;
}
/// <summary>
/// Overload for backwards compatibility which only tries to load
/// assemblies that are already loaded in memory.
/// </summary>
/// <param name="typeName"></param>
/// <returns></returns>
public static Type GetTypeFromName(string typeName)
{
return GetTypeFromName(typeName, null);
}
/// <summary>
/// Creates a COM instance from a ProgID. Loads either
/// Exe or DLL servers.
/// </summary>
/// <param name="progId"></param>
/// <returns></returns>
public static object CreateComInstance(string progId)
{
Type type = Type.GetTypeFromProgID(progId);
if (type == null)
return null;
return Activator.CreateInstance(type);
}
/// <summary>
/// Try to load an assembly into the application's app domain.
/// Loads by name first then checks for filename
/// </summary>
/// <param name="assemblyName">Assembly name or full path</param>
/// <returns>null on failure</returns>
public static Assembly LoadAssembly(string assemblyName)
{
Assembly assembly = null;
try
{
assembly = Assembly.Load(assemblyName);
}
catch { }
if (assembly != null)
return assembly;
if (File.Exists(assemblyName))
{
assembly = Assembly.LoadFrom(assemblyName);
if (assembly != null)
return assembly;
}
return null;
}
#endregion
#region Conversions
/// <summary>
/// Turns a string into a typed value generically.
/// Explicitly assigns common types and falls back
/// on using type converters for unhandled types.
///
/// Common uses:
/// * UI -&gt; to data conversions
/// * Parsers
/// <seealso>Class ReflectionUtils</seealso>
/// </summary>
/// <param name="sourceString">
/// The string to convert from
/// </param>
/// <param name="targetType">
/// The type to convert to
/// </param>
/// <param name="culture">
/// Culture used for numeric and datetime values.
/// </param>
/// <returns>object. Throws exception if it cannot be converted.</returns>
public static object StringToTypedValue(string sourceString, Type targetType, CultureInfo culture = null)
{
object result = null;
bool isEmpty = string.IsNullOrEmpty(sourceString);
if (culture == null)
culture = CultureInfo.CurrentCulture;
if (targetType == typeof(string))
result = sourceString;
else if (targetType == typeof(Int32) || targetType == typeof(int))
{
if (isEmpty)
result = 0;
else
result = Int32.Parse(sourceString, NumberStyles.Any, culture.NumberFormat);
}
else if (targetType == typeof(Int64))
{
if (isEmpty)
result = (Int64)0;
else
result = Int64.Parse(sourceString, NumberStyles.Any, culture.NumberFormat);
}
else if (targetType == typeof(Int16))
{
if (isEmpty)
result = (Int16)0;
else
result = Int16.Parse(sourceString, NumberStyles.Any, culture.NumberFormat);
}
else if (targetType == typeof(decimal))
{
if (isEmpty)
result = 0M;
else
result = decimal.Parse(sourceString, NumberStyles.Any, culture.NumberFormat);
}
else if (targetType == typeof(DateTime))
{
if (isEmpty)
result = DateTime.MinValue;
else
result = Convert.ToDateTime(sourceString, culture.DateTimeFormat);
}
else if (targetType == typeof(byte))
{
if (isEmpty)
result = 0;
else
result = Convert.ToByte(sourceString);
}
else if (targetType == typeof(double))
{
if (isEmpty)
result = 0F;
else
result = Double.Parse(sourceString, NumberStyles.Any, culture.NumberFormat);
}
else if (targetType == typeof(Single))
{
if (isEmpty)
result = 0F;
else
result = Single.Parse(sourceString, NumberStyles.Any, culture.NumberFormat);
}
else if (targetType == typeof(bool))
{
sourceString = sourceString.ToLower();
if (!isEmpty &&
sourceString == "true" || sourceString == "on" ||
sourceString == "1" || sourceString == "yes")
result = true;
else
result = false;
}
else if (targetType == typeof(Guid))
{
if (isEmpty)
result = Guid.Empty;
else
result = new Guid(sourceString);
}
else if (targetType.IsEnum)
result = Enum.Parse(targetType, sourceString);
else if (targetType == typeof(byte[]))
{
// TODO: Convert HexBinary string to byte array
result = null;
}
// Handle nullables explicitly since type converter won't handle conversions
// properly for things like decimal separators currency formats etc.
// Grab underlying type and pass value to that
else if (targetType.Name.StartsWith("Nullable`"))
{
if (sourceString.ToLower() == "null" || sourceString == string.Empty)
result = null;
else
{
targetType = Nullable.GetUnderlyingType(targetType);
result = StringToTypedValue(sourceString, targetType);
}
}
else
{
TypeConverter converter = TypeDescriptor.GetConverter(targetType);
if (converter != null && converter.CanConvertFrom(typeof(string)))
result = converter.ConvertFromString(null, culture, sourceString);
else
{
Debug.Assert(false, string.Format("Type Conversion not handled in StringToTypedValue for {0} {1}",
targetType.Name, sourceString));
throw (new InvalidCastException(Resources.StringToTypedValueValueTypeConversionFailed + targetType.Name));
}
}
return result;
}
/// <summary>
/// Generic version allow for automatic type conversion without the explicit type
/// parameter
/// </summary>
/// <typeparam name="T">Type to be converted to</typeparam>
/// <param name="sourceString">input string value to be converted</param>
/// <param name="culture">Culture applied to conversion</param>
/// <returns></returns>
public static T StringToTypedValue<T>(string sourceString, CultureInfo culture = null)
{
return (T)StringToTypedValue(sourceString, typeof(T), culture);
}
/// <summary>
/// Converts a type to string if possible. This method supports an optional culture generically on any value.
/// It calls the ToString() method on common types and uses a type converter on all other objects
/// if available
/// </summary>
/// <param name="rawValue">The Value or Object to convert to a string</param>
/// <param name="culture">Culture for numeric and DateTime values</param>
/// <param name="unsupportedReturn">Return string for unsupported types</param>
/// <returns>string</returns>
public static string TypedValueToString(object rawValue, CultureInfo culture = null, string unsupportedReturn = null)
{
if (rawValue == null)
return string.Empty;
if (culture == null)
culture = CultureInfo.CurrentCulture;
Type valueType = rawValue.GetType();
string returnValue = null;
if (valueType == typeof(string))
returnValue = rawValue as string;
else if (valueType == typeof(int) || valueType == typeof(decimal) ||
valueType == typeof(double) || valueType == typeof(float) || valueType == typeof(Single))
returnValue = string.Format(culture.NumberFormat, "{0}", rawValue);
else if (valueType == typeof(DateTime))
returnValue = string.Format(culture.DateTimeFormat, "{0}", rawValue);
else if (valueType == typeof(bool) || valueType == typeof(Byte) || valueType.IsEnum)
returnValue = rawValue.ToString();
else if (valueType == typeof(Guid?))
{
if (rawValue == null)
returnValue = string.Empty;
else
return rawValue.ToString();
}
else
{
// Any type that supports a type converter
TypeConverter converter = TypeDescriptor.GetConverter(valueType);
if (converter != null && converter.CanConvertTo(typeof(string)) && converter.CanConvertFrom(typeof(string)))
returnValue = converter.ConvertToString(null, culture, rawValue);
else
{
// Last resort - just call ToString() on unknown type
if (!string.IsNullOrEmpty(unsupportedReturn))
returnValue = unsupportedReturn;
else
returnValue = rawValue.ToString();
}
}
return returnValue;
}
#endregion
#region Member Access
/// <summary>
/// Retrieve a field dynamically from an object. This is a simple implementation that's
/// straight Reflection and doesn't support indexers.
/// </summary>
/// <param name="Object">Object to retreve Field from</param>
/// <param name="Property">name of the field to retrieve</param>
/// <returns></returns>
public static object GetField(object Object, string Property)
{
return Object.GetType().GetField(Property, ReflectionUtils.MemberAccess).GetValue(Object);
}
/// <summary>
/// Parses Properties and Fields including Array and Collection references.
/// Used internally for the 'Ex' Reflection methods.
/// </summary>
/// <param name="Parent"></param>
/// <param name="Property"></param>
/// <returns></returns>
private static object GetPropertyInternal(object Parent, string Property)
{
if (Property == "this" || Property == "me")
return Parent;
object result = null;
string pureProperty = Property;
string indexes = null;
bool isArrayOrCollection = false;
// Deal with Array Property
if (Property.IndexOf("[") > -1)
{
pureProperty = Property.Substring(0, Property.IndexOf("["));
indexes = Property.Substring(Property.IndexOf("["));
isArrayOrCollection = true;
}
// Get the member
MemberInfo member = Parent.GetType().GetMember(pureProperty, ReflectionUtils.MemberAccess)[0];
if (member.MemberType == MemberTypes.Property)
result = ((PropertyInfo)member).GetValue(Parent, null);
else
result = ((FieldInfo)member).GetValue(Parent);
if (isArrayOrCollection)
{
indexes = indexes.Replace("[", string.Empty).Replace("]", string.Empty);
if (result is Array)
{
int Index = -1;
int.TryParse(indexes, out Index);
result = CallMethod(result, "GetValue", Index);
}
else if (result is ICollection)
{
if (indexes.StartsWith("\""))
{
// String Index
indexes = indexes.Trim('\"');
result = CallMethod(result, "get_Item", indexes);
}
else
{
// assume numeric index
int index = -1;
int.TryParse(indexes, out index);
result = CallMethod(result, "get_Item", index);
}
}
}
return result;
}
/// <summary>
/// Parses Properties and Fields including Array and Collection references.
/// </summary>
/// <param name="Parent"></param>
/// <param name="Property"></param>
/// <returns></returns>
private static object SetPropertyInternal(object Parent, string Property, object Value)
{
if (Property == "this" || Property == "me")
return Parent;
object Result = null;
string PureProperty = Property;
string Indexes = null;
bool IsArrayOrCollection = false;
// Deal with Array Property
if (Property.IndexOf("[") > -1)
{
PureProperty = Property.Substring(0, Property.IndexOf("["));
Indexes = Property.Substring(Property.IndexOf("["));
IsArrayOrCollection = true;
}
if (!IsArrayOrCollection)
{
// Get the member
MemberInfo Member = Parent.GetType().GetMember(PureProperty, ReflectionUtils.MemberAccess)[0];
if (Member.MemberType == MemberTypes.Property)
((PropertyInfo)Member).SetValue(Parent, Value, null);
else
((FieldInfo)Member).SetValue(Parent, Value);
return null;
}
else
{
// Get the member
MemberInfo Member = Parent.GetType().GetMember(PureProperty, ReflectionUtils.MemberAccess)[0];
if (Member.MemberType == MemberTypes.Property)
Result = ((PropertyInfo)Member).GetValue(Parent, null);
else
Result = ((FieldInfo)Member).GetValue(Parent);
}
if (IsArrayOrCollection)
{
Indexes = Indexes.Replace("[", string.Empty).Replace("]", string.Empty);
if (Result is Array)
{
int Index = -1;
int.TryParse(Indexes, out Index);
Result = CallMethod(Result, "SetValue", Value, Index);
}
else if (Result is ICollection)
{
if (Indexes.StartsWith("\""))
{
// String Index
Indexes = Indexes.Trim('\"');
Result = CallMethod(Result, "set_Item", Indexes, Value);
}
else
{
// assume numeric index
int Index = -1;
int.TryParse(Indexes, out Index);
Result = CallMethod(Result, "set_Item", Index, Value);
}
}
}
return Result;
}
/// <summary>
/// Returns a property or field value using a base object and sub members including . syntax.
/// For example, you can access: oCustomer.oData.Company with (this,"oCustomer.oData.Company")
/// This method also supports indexers in the Property value such as:
/// Customer.DataSet.Tables["Customers"].Rows[0]
/// </summary>
/// <param name="Parent">Parent object to 'start' parsing from. Typically this will be the Page.</param>
/// <param name="Property">The property to retrieve. Example: 'Customer.Entity.Company'</param>
/// <returns></returns>
public static object GetPropertyEx(object Parent, string Property)
{
Type type = Parent.GetType();
int at = Property.IndexOf(".");
if (at < 0)
{
// Complex parse of the property
return GetPropertyInternal(Parent, Property);
}
// Walk the . syntax - split into current object (Main) and further parsed objects (Subs)
string main = Property.Substring(0, at);
string subs = Property.Substring(at + 1);
// Retrieve the next . section of the property
object sub = GetPropertyInternal(Parent, main);
// Now go parse the left over sections
return GetPropertyEx(sub, subs);
}
/// <summary>
/// Returns a PropertyInfo object for a given dynamically accessed property
///
/// Property selection can be specified using . syntax ("Address.Street" or "DataTable[0].Rows[1]") hence the 'Ex' name for this function.
/// </summary>
/// <param name="Parent"></param>
/// <param name="Property"></param>
/// <returns></returns>
public static PropertyInfo GetPropertyInfoEx(object Parent, string Property)
{
Type type = Parent.GetType();
int at = Property.IndexOf(".");
if (at < 0)
{
// Complex parse of the property
return GetPropertyInfoInternal(Parent, Property);
}
// Walk the . syntax - split into current object (Main) and further parsed objects (Subs)
string main = Property.Substring(0, at);
string subs = Property.Substring(at + 1);
// Retrieve the next . section of the property
object sub = GetPropertyInternal(Parent, main);
// Now go parse the left over sections
return GetPropertyInfoEx(sub, subs);
}
/// <summary>
/// Returns a PropertyInfo structure from an extended Property reference
/// </summary>
/// <param name="Parent"></param>
/// <param name="Property"></param>
/// <returns></returns>
public static PropertyInfo GetPropertyInfoInternal(object Parent, string Property)
{
if (Property == "this" || Property == "me")
return null;
string propertyName = Property;
// Deal with Array Property - strip off array indexer
if (Property.IndexOf("[") > -1)
propertyName = Property.Substring(0, Property.IndexOf("["));
// Get the member
return Parent.GetType().GetProperty(propertyName, ReflectionUtils.MemberAccess);
}
/// <summary>
/// Sets the property on an object. This is a simple method that uses straight Reflection
/// and doesn't support indexers.
/// </summary>
/// <param name="obj">Object to set property on</param>
/// <param name="property">Name of the property to set</param>
/// <param name="value">value to set it to</param>
public static void SetProperty(object obj, string property, object value)
{
obj.GetType().GetProperty(property, ReflectionUtils.MemberAccess).SetValue(obj, value, null);
}
/// <summary>
/// Sets the field on an object. This is a simple method that uses straight Reflection
/// and doesn't support indexers.
/// </summary>
/// <param name="obj">Object to set property on</param>
/// <param name="property">Name of the field to set</param>
/// <param name="value">value to set it to</param>
public static void SetField(object obj, string property, object value)
{
obj.GetType().GetField(property, ReflectionUtils.MemberAccess).SetValue(obj, value);
}
/// <summary>
/// Sets a value on an object. Supports . syntax for named properties
/// (ie. Customer.Entity.Company) as well as indexers.
/// </summary>
/// <param name="Object Parent">
/// Object to set the property on.
/// </param>
/// <param name="String Property">
/// Property to set. Can be an object hierarchy with . syntax and can
/// include indexers. Examples: Customer.Entity.Company,
/// Customer.DataSet.Tables["Customers"].Rows[0]
/// </param>
/// <param name="Object Value">
/// Value to set the property to
/// </param>
public static object SetPropertyEx(object parent, string property, object value)
{
Type Type = parent.GetType();
// no more .s - we got our final object
int lnAt = property.IndexOf(".");
if (lnAt < 0)
{
SetPropertyInternal(parent, property, value);
return null;
}
// Walk the . syntax
string Main = property.Substring(0, lnAt);
string Subs = property.Substring(lnAt + 1);
object Sub = GetPropertyInternal(parent, Main);
SetPropertyEx(Sub, Subs, value);
return null;
}
/// <summary>
/// Calls a method on an object dynamically. This version requires explicit
/// specification of the parameter type signatures.
/// </summary>
/// <param name="instance">Instance of object to call method on</param>
/// <param name="method">The method to call as a stringToTypedValue</param>
/// <param name="parameterTypes">Specify each of the types for each parameter passed.
/// You can also pass null, but you may get errors for ambiguous methods signatures
/// when null parameters are passed</param>
/// <param name="parms">any variable number of parameters.</param>
/// <returns>object</returns>
public static object CallMethod(object instance, string method, Type[] parameterTypes, params object[] parms)
{
if (parameterTypes == null && parms.Length > 0)
// Call without explicit parameter types - might cause problems with overloads
// occurs when null parameters were passed and we couldn't figure out the parm type
return instance.GetType().GetMethod(method, ReflectionUtils.MemberAccess | BindingFlags.InvokeMethod).Invoke(instance, parms);
else
// Call with parameter types - works only if no null values were passed
return instance.GetType().GetMethod(method, ReflectionUtils.MemberAccess | BindingFlags.InvokeMethod, null, parameterTypes, null).Invoke(instance, parms);
}
/// <summary>
/// Calls a method on an object dynamically.
///
/// This version doesn't require specific parameter signatures to be passed.
/// Instead parameter types are inferred based on types passed. Note that if
/// you pass a null parameter, type inferrance cannot occur and if overloads
/// exist the call may fail. if so use the more detailed overload of this method.
/// </summary>
/// <param name="instance">Instance of object to call method on</param>
/// <param name="method">The method to call as a stringToTypedValue</param>
/// <param name="parameterTypes">Specify each of the types for each parameter passed.
/// You can also pass null, but you may get errors for ambiguous methods signatures
/// when null parameters are passed</param>
/// <param name="parms">any variable number of parameters.</param>
/// <returns>object</returns>
public static object CallMethod(object instance, string method, params object[] parms)
{
// Pick up parameter types so we can match the method properly
Type[] parameterTypes = null;
if (parms != null)
{
parameterTypes = new Type[parms.Length];
for (int x = 0; x < parms.Length; x++)
{
// if we have null parameters we can't determine parameter types - exit
if (parms[x] == null)
{
parameterTypes = null; // clear out - don't use types
break;
}
parameterTypes[x] = parms[x].GetType();
}
}
return CallMethod(instance, method, parameterTypes, parms);
}
/// <summary>
/// Calls a method on an object with extended . syntax (object: this Method: Entity.CalculateOrderTotal)
/// </summary>
/// <param name="parent"></param>
/// <param name="method"></param>
/// <param name="params"></param>
/// <returns></returns>
public static object CallMethodEx(object parent, string method, params object[] parms)
{
Type Type = parent.GetType();
// no more .s - we got our final object
int lnAt = method.IndexOf(".");
if (lnAt < 0)
{
return ReflectionUtils.CallMethod(parent, method, parms);
}
// Walk the . syntax
string Main = method.Substring(0, lnAt);
string Subs = method.Substring(lnAt + 1);
object Sub = GetPropertyInternal(parent, Main);
// Recurse until we get the lowest ref
return CallMethodEx(Sub, Subs, parms);
}
/// <summary>
/// Retrieves a value from a static property by specifying a type full name and property
/// </summary>
/// <param name="typeName">Full type name (namespace.class)</param>
/// <param name="property">Property to get value from</param>
/// <returns></returns>
public static object GetStaticProperty(string typeName, string property)
{
Type type = GetTypeFromName(typeName);
if (type == null)
return null;
return GetStaticProperty(type, property);
}
/// <summary>
/// Returns a static property from a given type
/// </summary>
/// <param name="type">Type instance for the static property</param>
/// <param name="property">Property name as a string</param>
/// <returns></returns>
public static object GetStaticProperty(Type type, string property)
{
object result = null;
try
{
result = type.InvokeMember(property, BindingFlags.Static | BindingFlags.Public | BindingFlags.GetField | BindingFlags.GetProperty, null, type, null);
}
catch
{
return null;
}
return result;
}
/// <summary>
/// Retrieve a property value from an object dynamically. This is a simple version
/// that uses Reflection calls directly. It doesn't support indexers.
/// </summary>
/// <param name="instance">Object to make the call on</param>
/// <param name="property">Property to retrieve</param>
/// <returns>Object - cast to proper type</returns>
public static object GetProperty(object instance, string property)
{
return instance.GetType().GetProperty(property, ReflectionUtils.MemberAccess).GetValue(instance, null);
}
/// <summary>
/// Returns a List of KeyValuePair object
/// </summary>
/// <param name="enumeration"></param>
/// <returns></returns>
public static List<KeyValuePair<string, string>> GetEnumList(Type enumType, bool valueAsFieldValueNumber = false)
{
//string[] enumStrings = Enum.GetNames(enumType);
Array enumValues = Enum.GetValues(enumType);
List<KeyValuePair<string, string>> items = new List<KeyValuePair<string, string>>();
foreach (var enumValue in enumValues)
{
var strValue = enumValue.ToString();
if (!valueAsFieldValueNumber)
items.Add(new KeyValuePair<string, string>(enumValue.ToString(), StringUtils.FromCamelCase(strValue)));
else
items.Add(new KeyValuePair<string, string>(((int)enumValue).ToString(),
StringUtils.FromCamelCase(strValue)
));
}
return items;
}
#endregion
#region COM Reflection Routines
/// <summary>
/// Retrieve a dynamic 'non-typelib' property
/// </summary>
/// <param name="instance">Object to make the call on</param>
/// <param name="property">Property to retrieve</param>
/// <returns></returns>
public static object GetPropertyCom(object instance, string property)
{
return instance.GetType().InvokeMember(property, ReflectionUtils.MemberAccess | BindingFlags.GetProperty | BindingFlags.GetField, null,
instance, null);
}
/// <summary>
/// Returns a property or field value using a base object and sub members including . syntax.
/// For example, you can access: oCustomer.oData.Company with (this,"oCustomer.oData.Company")
/// </summary>
/// <param name="parent">Parent object to 'start' parsing from.</param>
/// <param name="property">The property to retrieve. Example: 'oBus.oData.Company'</param>
/// <returns></returns>
public static object GetPropertyExCom(object parent, string property)
{
Type Type = parent.GetType();
int lnAt = property.IndexOf(".");
if (lnAt < 0)
{
if (property == "this" || property == "me")
return parent;
// Get the member
return parent.GetType().InvokeMember(property, ReflectionUtils.MemberAccess | BindingFlags.GetProperty | BindingFlags.GetField, null,
parent, null);
}
// Walk the . syntax - split into current object (Main) and further parsed objects (Subs)
string Main = property.Substring(0, lnAt);
string Subs = property.Substring(lnAt + 1);
object Sub = parent.GetType().InvokeMember(Main, ReflectionUtils.MemberAccess | BindingFlags.GetProperty | BindingFlags.GetField, null,
parent, null);
// Recurse further into the sub-properties (Subs)
return ReflectionUtils.GetPropertyExCom(Sub, Subs);
}
/// <summary>
/// Sets the property on an object.
/// </summary>
/// <param name="Object">Object to set property on</param>
/// <param name="Property">Name of the property to set</param>
/// <param name="Value">value to set it to</param>
public static void SetPropertyCom(object Object, string Property, object Value)
{
Object.GetType().InvokeMember(Property, ReflectionUtils.MemberAccess | BindingFlags.SetProperty | BindingFlags.SetField, null, Object, new object[1] { Value });
}
/// <summary>
/// Sets the value of a field or property via Reflection. This method alws
/// for using '.' syntax to specify objects multiple levels down.
///
/// ReflectionUtils.SetPropertyEx(this,"Invoice.LineItemsCount",10)
///
/// which would be equivalent of:
///
/// Invoice.LineItemsCount = 10;
/// </summary>
/// <param name="Object Parent">
/// Object to set the property on.
/// </param>
/// <param name="String Property">
/// Property to set. Can be an object hierarchy with . syntax.
/// </param>
/// <param name="Object Value">
/// Value to set the property to
/// </param>
public static object SetPropertyExCom(object parent, string property, object value)
{
Type Type = parent.GetType();
int lnAt = property.IndexOf(".");
if (lnAt < 0)
{
// Set the member
parent.GetType().InvokeMember(property, ReflectionUtils.MemberAccess | BindingFlags.SetProperty | BindingFlags.SetField, null,
parent, new object[1] { value });
return null;
}
// Walk the . syntax - split into current object (Main) and further parsed objects (Subs)
string Main = property.Substring(0, lnAt);
string Subs = property.Substring(lnAt + 1);
object Sub = parent.GetType().InvokeMember(Main, ReflectionUtils.MemberAccess | BindingFlags.GetProperty | BindingFlags.GetField, null,
parent, null);
return SetPropertyExCom(Sub, Subs, value);
}
/// <summary>
/// Wrapper method to call a 'dynamic' (non-typelib) method
/// on a COM object
/// </summary>
/// <param name="params"></param>
/// 1st - Method name, 2nd - 1st parameter, 3rd - 2nd parm etc.
/// <returns></returns>
public static object CallMethodCom(object instance, string method, params object[] parms)
{
return instance.GetType().InvokeMember(method, ReflectionUtils.MemberAccess | BindingFlags.InvokeMethod, null, instance, parms);
}
/// <summary>
/// Calls a method on a COM object with '.' syntax (Customer instance and Address.DoSomeThing method)
/// </summary>
/// <param name="parent">the object instance on which to call method</param>
/// <param name="method">The method or . syntax path to the method (Address.Parse)</param>
/// <param name="parms">Any number of parameters</param>
/// <returns></returns>
public static object CallMethodExCom(object parent, string method, params object[] parms)
{
Type Type = parent.GetType();
// no more .s - we got our final object
int at = method.IndexOf(".");
if (at < 0)
{
return ReflectionUtils.CallMethod(parent, method, parms);
}
// Walk the . syntax - split into current object (Main) and further parsed objects (Subs)
string Main = method.Substring(0, at);
string Subs = method.Substring(at + 1);
object Sub = parent.GetType().InvokeMember(Main, ReflectionUtils.MemberAccess | BindingFlags.GetProperty | BindingFlags.GetField, null,
parent, null);
// Recurse until we get the lowest ref
return CallMethodEx(Sub, Subs, parms);
}
#endregion
}
}