using System;
using System.Collections.Generic;
using System.ComponentModel;
using System.Diagnostics;
using System.Globalization;
using System.IO;
using System.Linq;
using System.Linq.Expressions;
using System.Reflection;
using System.Runtime.InteropServices;
using System.Runtime.Serialization;
using JD.Kernel.Classes;
using JD.Kernel.StaticHelpers;
using JetBrains.Annotations;
namespace JD.Reflection
{
///
/// Helper class.
///
public static class ReflectionHelper
{
#region Variables
private static readonly List m_integerTypes = new List();
private static readonly List m_floatingPointTypes = new List();
private static readonly TypeConverterCache TypeConverter = new TypeConverterCache();
#endregion
#region Constructor
///
/// Static constructor.
///
static ReflectionHelper()
{
// Integer types.
m_integerTypes.Add(typeof(byte));
m_integerTypes.Add(typeof(sbyte));
m_integerTypes.Add(typeof(short));
m_integerTypes.Add(typeof(ushort));
m_integerTypes.Add(typeof(int));
m_integerTypes.Add(typeof(uint));
m_integerTypes.Add(typeof(long));
m_integerTypes.Add(typeof(ulong));
// Floating-point types.
m_floatingPointTypes.Add(typeof(float));
m_floatingPointTypes.Add(typeof(double));
m_floatingPointTypes.Add(typeof(decimal));
}
#endregion
#region Methods
#region Type Helpers
///
/// Set a value of a member via reflection
///
///
///
///
public static void SetValue(object instance, string memberName, object value)
{
foreach (ValueMemberInfo valueMemberInfo in ValueMemberInfo.GetValueMembers(instance.GetType(), true))
{
if (valueMemberInfo.Name.Equals(memberName, StringComparison.InvariantCultureIgnoreCase))
{
valueMemberInfo.SetValue(instance, value);
}
}
}
///
/// Gets the first attribute of type TAttribute decorating the type
///
public static TAttribute GetAttribute(this ICustomAttributeProvider type)
{
return GetTypeAttributes(type).OfType().FirstOrDefault();
}
///
/// Gets the first attribute of type TAttribute decorating the type
///
public static TAttribute GetAttribute(this ICustomAttributeProvider type, bool inheritable /* = false */)
{
return GetTypeAttributes(type, inheritable).OfType().FirstOrDefault();
}
///
/// Gets the custom attributes of a C# type.
///
///
///
public static IEnumerable GetTypeAttributes(ICustomAttributeProvider type)
{
return GetTypeAttributes(type, false);
}
///
/// Gets the custom attributes of a C# type.
///
public static IEnumerable GetTypeAttributes(ICustomAttributeProvider type, bool inheritable /* = false */)
{
Throw.IfNull(type, "type");
return type.GetCustomAttributes(inheritable).Cast();
}
///
/// Returns true if the given is a numerical type.
///
///
/// The complete list of numerical types is:
///
/// - byte
/// - sbyte
/// - short
/// - ushort
/// - int
/// - uint
/// - long
/// - ulong
/// - float
/// - double
/// - decimal
///
///
/// Type to test for.
/// True if the given is a numerical type. False otherwise.
public static bool IsNumerical(Type type)
{
return (IsInteger(type) || IsFloatingPoint(type));
}
///
/// Returns true if the given is a floating-point numerical type.
///
///
/// The complete list of floating-point numerical types is:
///
/// - float
/// - double
/// - decimal
///
///
/// Type to test for.
/// True if the given is a floating-point numerical type. False otherwise.
public static bool IsFloatingPoint(Type type)
{
return m_floatingPointTypes.Contains(type);
}
///
/// Returns true if the given is an integer numerical type.
///
///
/// The complete list of integer numerical types is:
///
/// - byte
/// - sbyte
/// - short
/// - ushort
/// - int
/// - uint
/// - long
/// - ulong
///
///
/// Type to test for.
/// True if the given is a integer numerical type. False otherwise.
public static bool IsInteger(Type type)
{
return m_integerTypes.Contains(type);
}
///
/// Returns true if the given is a signed integer numerical type.
///
///
///
public static bool IsSignedInteger(Type type)
{
return type == typeof(long) || type == typeof(int) || type == typeof(short) || type == typeof(sbyte);
}
public static bool IsList(Type type)
{
int found = (from i in type.GetInterfaces()
where i.IsGenericType && i.GetGenericTypeDefinition() == typeof(IList<>)
select i).Count();
return found > 0;
}
public static bool IsString(Type type)
{
return type == typeof(string);
}
public static object ConvertValue(object value, Type destinationType)
{
object result;
string msg;
if (!TryConvertValue(value, destinationType, out result, out msg))
{
throw new InvalidCastException(msg);
}
return result;
}
public static T ConvertValue(object value)
{
return (T)ConvertValue(value, typeof(T));
}
public static bool TryConvertValue(object value, Type destinationType, out object result, out string errorMsg)
{
if (TryConvertValue(value, destinationType, out result))
{
errorMsg = string.Empty;
return true;
}
errorMsg = $"Cannot convert {value} from {GetUserFriendlyTypeName(value.GetType())} to {GetUserFriendlyTypeName(destinationType)}";
return false;
}
///
/// Tries to convert from the value to the destination type. Returns false if unable.
///
public static bool TryConvertValue(object value, Type destinationType, out object result)
{
result = value;
if (value.GetType() == destinationType)
{
return true;
}
if (value is string)
{
string stringValue = value.ToString();
if (IsInteger(destinationType))
{
if (stringValue.StartsWith("0x", StringComparison.OrdinalIgnoreCase))
{
if (!TryConvertHexStringToInteger(stringValue, destinationType, out value))
{
return false;
}
}
else
{
Int64 intValue;
if (Int64.TryParse(stringValue, out intValue))
{
value = intValue;
}
else
{
return false;
}
}
}
else
{
return TryConvertFromString(stringValue, destinationType, out result);
}
}
value = MatchSign(value, destinationType);
try
{
result = Convert.ChangeType(value, destinationType, CultureInfo.InvariantCulture);
return true;
}
catch
{
result = null;
return false;
}
}
private static bool TryConvertHexStringToInteger(string stringValue, Type destinationType, out object value)
{
stringValue = stringValue.Substring(2);
int size = Marshal.SizeOf(destinationType);
switch (size)
{
case 1:
if (!TryParseHexByte(stringValue, out value))
{
return false;
}
break;
case 2:
if (!TryParseHexUShort(stringValue, out value))
{
return false;
}
break;
case 4:
if (!TryParseHexUInt(stringValue, out value))
{
return false;
}
break;
case 8:
if (!TryParseHexULong(stringValue, out value))
{
return false;
}
break;
default:
throw new NotSupportedException();
}
return true;
}
private static bool TryConvertFromString(string value, Type destinationType, out object convertedValue)
{
convertedValue = null;
if (value == null && destinationType.IsValueType)
{
return false;
}
if (destinationType == typeof(int))
{
int numberValue;
bool result = int.TryParse(value, out numberValue);
convertedValue = numberValue;
return result;
}
else if (destinationType == typeof(float))
{
float numberValue;
bool result = float.TryParse(value, NumberStyles.Float, CultureInfo.InvariantCulture, out numberValue);
convertedValue = numberValue;
return result;
}
TypeConverter converter = TypeConverter.GetConverter(destinationType);
if (!converter.CanConvertFrom(typeof(string)))
{
return false;
}
try
{
convertedValue = converter.ConvertFromInvariantString(value);
return true;
}
catch
{
return false;
}
}
private static bool TryParseHexULong(string hexValue, out object value)
{
ulong outValue;
if (!ulong.TryParse(hexValue, NumberStyles.HexNumber, CultureInfo.InvariantCulture, out outValue))
{
value = null;
return false;
}
value = outValue;
return true;
}
private static bool TryParseHexUInt(string hexValue, out object value)
{
uint outValue;
if (!uint.TryParse(hexValue, NumberStyles.HexNumber, CultureInfo.InvariantCulture, out outValue))
{
value = null;
return false;
}
value = outValue;
return true;
}
private static bool TryParseHexUShort(string hexValue, out object value)
{
ushort outValue;
if (!ushort.TryParse(hexValue, NumberStyles.HexNumber, CultureInfo.InvariantCulture, out outValue))
{
value = null;
return false;
}
value = outValue;
return true;
}
private static bool TryParseHexByte(string hexValue, out object value)
{
byte outValue;
if (!byte.TryParse(hexValue, NumberStyles.HexNumber, CultureInfo.InvariantCulture, out outValue))
{
value = null;
return false;
}
value = outValue;
return true;
}
private static object MatchSign(object value, Type destinationType)
{
if (value != null && IsInteger(destinationType) && IsInteger(value.GetType()))
{
bool sourceSigned = IsSignedInteger(value.GetType());
bool targetSigned = IsSignedInteger(destinationType);
if (sourceSigned != targetSigned)
{
value = ConvertSign(value, targetSigned);
}
}
return value;
}
private static object ConvertSign(object value, bool signed)
{
Debug.Assert(value != null);
int size = Marshal.SizeOf(value.GetType());
unchecked
{
switch (size)
{
case 1:
if (signed)
{
return (sbyte)(byte)value;
}
else
{
return (byte)(sbyte)value;
}
case 2:
if (signed)
{
return (short)(ushort)value;
}
else
{
return (ushort)(short)value;
}
case 4:
if (signed)
{
return (int)(uint)value;
}
else
{
return (uint)(int)value;
}
case 8:
if (signed)
{
return (long)(ulong)value;
}
else
{
return (ulong)(long)value;
}
default:
throw new NotImplementedException();
}
}
}
private static string GetUserFriendlyTypeName(Type type)
{
if (type == typeof(string))
{
return "an alphanumerical value";
}
if (ReflectionHelper.IsNumerical(type))
{
return "a numerical value";
}
return "a " + type.Name;
}
#endregion
#region Reflection
///
/// Gets an attribute on the given enum .
///
[NotNull]
public static TAttribute Attribute(this Enum element)
where TAttribute : Attribute
{
var result = GetEnumAttribute(element);
if (result == null)
{
string error = $"{typeof(TAttribute).Name} is not defined for {element.GetType().Name}.{element}";
throw new ApplicationException(error);
}
return result;
}
///
/// Gets an attribute on the given enum .
///
///
///
public static TAttribute GetEnumAttribute(Enum element)
where TAttribute : Attribute
{
Throw.IfNull(element, "element");
FieldInfo fieldInfo = element.GetType().GetField(element.ToString());
if (fieldInfo == null)
{
return null;
}
TAttribute[] attributes = (TAttribute[])fieldInfo.GetCustomAttributes(typeof(TAttribute), false);
if (attributes.Length > 0)
{
return attributes[0];
}
return null;
}
///
/// Gets a description for the given .
///
///
///
public static string GetEnumDescription(Enum element)
{
DescriptionAttribute description = GetEnumAttribute(element);
return description == null ? element.ToString() : description.Description;
}
#region Component model
///
/// Compares two objects for equality using their browsable properties.
///
public static bool AreEqual(object a, object b)
{
Throw.Assert(a.GetType() == b.GetType());
foreach (ValueMemberInfo valueMemberInfo in GetBrowsableMembers(a.GetType()))
{
if (!Equals(valueMemberInfo.GetValue(a), valueMemberInfo.GetValue(b)))
{
return false;
}
}
return true;
}
private static IEnumerable GetBrowsableMembers(Type type)
{
foreach (ValueMemberInfo valueMemberInfo in ValueMemberInfo.GetValueMembers(type, true))
{
BrowsableAttribute browsableAttribute = valueMemberInfo.GetAttribute(true);
if (browsableAttribute != null && browsableAttribute.Browsable)
{
yield return valueMemberInfo;
}
}
}
#endregion
#endregion
#region String Formatting
///
/// Converts the given into a string, for display in the property grid.
///
///
public static string FormatStringForPropertyGrid(object value)
{
if (ReferenceEquals(value, null))
{
return string.Empty;
}
if (value is IFormattable && IsFloatingPoint(value.GetType()))
{
var convertedValue = ConvertValue(value);
if (convertedValue > 0 && convertedValue < 0.00000001)
{
return "0.00000000";
}
if (convertedValue < 0 && convertedValue > -0.00000001)
{
return "-0.00000000";
}
return ((IFormattable)value).ToString("0.########", CultureInfo.InvariantCulture);
}
return value.ToString();
}
#endregion
#region Validation
#endregion
#region Exception Handling
///
/// Gets the types contained in the assembly.
///
///
/// Returns the result of assembly.GetTypes() but throws a better exception when the types cannot be loaded.
///
///
///
[DebuggerNonUserCode]
public static Type[] GetTypes(Assembly assembly)
{
try
{
return assembly.GetTypes();
}
catch (ReflectionTypeLoadException e)
{
if (e.LoaderExceptions.OfType().Any())
{
string msgDependencyMissing =
$@"Cannot load assembly {assembly.FullName} because it is missing dependencies. Make sure all dependencies (including unmanaged dlls) are present in the output directory. You can use depends.exe to diagnose this.";
throw new AssemblyLoadException(msgDependencyMissing, e);
}
string msg = $@"Cannot load types for assembly {assembly.FullName}. This is probably due to one of these problems:
\tOne of the assembly's dependency is missing. Make sure all dependencies (including unmanaged dlls) are present in the output directory.
\tThere is a version mismatch between the assembly and its dependencies. Make sure all assemblies in the output directory are up-to-date (a 'Build Solution' might fix this)
You can inspect the LoaderExceptions property on this exception for more information.";
throw new AssemblyLoadException(msg, e);
}
}
[Serializable]
public class AssemblyLoadException : Exception
{
#region Constructor
public AssemblyLoadException()
{
}
public AssemblyLoadException(string message)
: base(message)
{
}
public AssemblyLoadException(string message, Exception inner)
: base(message, inner)
{
}
protected AssemblyLoadException(
SerializationInfo info,
StreamingContext context)
: base(info, context)
{
}
#endregion
}
#endregion
#region Lambda magic
public static MethodInfo ResolveMethod(Expression expression)
{
Throw.IfNull(expression, "expression");
Throw.InvalidArgumentIf(!(expression.Body is MethodCallExpression), "Cannot resolve a method from this expression", "expression");
var methodExpression = (MethodCallExpression)expression.Body;
return methodExpression.Method;
}
#endregion
#endregion
}
}