Created
March 29, 2013 15:35
-
-
Save gabrieljoelc/5271577 to your computer and use it in GitHub Desktop.
Adapted from parts of http://dnpextensions.codeplex.com/ and https://code.google.com/p/gim-projects/source/browse/presentations/CantDanceTheLambda/src/MemberNameParser.cs with some additions of my own and coworkers.
This file contains hidden or bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
using System; | |
using System.Collections.Generic; | |
using System.ComponentModel; | |
using System.ComponentModel.DataAnnotations; | |
using System.Linq; | |
using System.Linq.Expressions; | |
using System.Reflection; | |
using System.Text; | |
using System.Text.RegularExpressions; | |
namespace ObjectUtilities | |
{ | |
// adapted from parts of http://dnpextensions.codeplex.com/ and | |
// https://code.google.com/p/gim-projects/source/browse/presentations/CantDanceTheLambda/src/MemberNameParser.cs | |
// with some additions of my own and coworkers. | |
/// <summary> | |
/// Extension methods for the root data type object | |
/// </summary> | |
public static class ObjectExtensions | |
{ | |
/// <summary> | |
/// Uses reflection to dump all the field and property values of an <paramref name="obj"/> and return a formatted string | |
/// </summary> | |
/// <param name="obj"></param> | |
/// <returns></returns> | |
public static string DumpToString(this object obj) | |
{ | |
if (obj == null) | |
{ | |
return "<Null>"; | |
} | |
var sb = new StringBuilder(); | |
var clazz = obj.GetType(); | |
sb.AppendFormat("--Type:<{0}>", clazz); | |
FieldInfo[] fields = clazz.GetFields(BindingFlags.Instance | BindingFlags.Public | BindingFlags.NonPublic | |
| BindingFlags.GetField); | |
for (int i = 0; i < fields.Length; i++) | |
{ | |
FieldInfo f = fields[i]; | |
if (!f.IsStatic) | |
{ | |
sb.AppendFormat("-Field <{0}> value <{1}>", f.Name, f.GetValue(obj) ?? "null"); | |
} | |
} | |
return sb.ToString(); | |
} | |
/// <summary> | |
/// Returns the name of a property, field or method on a given object | |
/// </summary> | |
/// <typeparam name="T"></typeparam> | |
/// <param name="obj"></param> | |
/// <param name="exp"></param> | |
/// <returns></returns> | |
/// <example> | |
/// <![CDATA[ | |
/// string somePropertyName = someObj.GetMemberName(s => s.SomeProperty); //Returns "SomeProperty" | |
/// ]]> | |
/// </example> | |
public static string GetMemberName<T>(this T obj, Expression<Func<T, object>> exp) | |
{ | |
return GetMemberNameImpl(exp.Body); | |
} | |
internal static string GetMemberNameImpl( | |
Expression expression) | |
{ | |
if (expression is MemberExpression) | |
{ | |
var memberExpression = (MemberExpression)expression; | |
if (memberExpression.Expression.NodeType == | |
ExpressionType.MemberAccess) | |
{ | |
return GetMemberNameImpl(memberExpression.Expression) | |
+ "." | |
+ memberExpression.Member.Name; | |
} | |
return memberExpression.Member.Name; | |
} | |
if (expression is UnaryExpression) | |
{ | |
var unaryExpression = (UnaryExpression)expression; | |
if (unaryExpression.NodeType != ExpressionType.Convert) | |
{ | |
throw new Exception(string.Format( | |
"Cannot interpret member from {0}", | |
expression)); | |
} | |
return GetMemberNameImpl(unaryExpression.Operand); | |
} | |
throw new Exception(string.Format( | |
"Could not determine member from {0}", | |
expression)); | |
} | |
/// <summary> | |
/// Converts an object to the specified target type or returns the default value. | |
/// </summary> | |
/// <typeparam name = "T"></typeparam> | |
/// <param name = "value">The value.</param> | |
/// <returns>The target type</returns> | |
public static T ConvertTo<T>(this object value) | |
{ | |
return value.ConvertTo(default(T)); | |
} | |
/// <summary> | |
/// Converts an object to the specified target type or returns the default value. | |
/// </summary> | |
/// <typeparam name = "T"></typeparam> | |
/// <param name = "value">The value.</param> | |
/// <param name = "defaultValue">The default value.</param> | |
/// <returns>The target type</returns> | |
public static T ConvertTo<T>(this object value, T defaultValue) | |
{ | |
if (value != null) | |
{ | |
Type targetType = typeof(T); | |
if (value.GetType() == targetType) | |
{ | |
return (T)value; | |
} | |
TypeConverter converter = TypeDescriptor.GetConverter(value); | |
if (converter != null) | |
{ | |
if (converter.CanConvertTo(targetType)) | |
{ | |
return (T)converter.ConvertTo(value, targetType); | |
} | |
} | |
converter = TypeDescriptor.GetConverter(targetType); | |
if (converter != null) | |
{ | |
if (converter.CanConvertFrom(value.GetType())) | |
{ | |
return (T)converter.ConvertFrom(value); | |
} | |
} | |
} | |
return defaultValue; | |
} | |
/// <summary> | |
/// Converts an object to the specified target type or returns the default value. Any exceptions are optionally ignored. | |
/// </summary> | |
/// <typeparam name = "T"></typeparam> | |
/// <param name = "value">The value.</param> | |
/// <param name = "defaultValue">The default value.</param> | |
/// <param name = "ignoreException">if set to <c>true</c> ignore any exception.</param> | |
/// <returns>The target type</returns> | |
public static T ConvertTo<T>(this object value, T defaultValue, bool ignoreException) | |
{ | |
if (ignoreException) | |
{ | |
try | |
{ | |
return value.ConvertTo<T>(); | |
} | |
catch (Exception) | |
{ | |
return defaultValue; | |
} | |
} | |
return value.ConvertTo<T>(); | |
} | |
/// <summary> | |
/// Determines whether the value can (in theory) be converted to the specified target type. | |
/// </summary> | |
/// <typeparam name = "T"></typeparam> | |
/// <param name = "value">The value.</param> | |
/// <returns> | |
/// <c>true</c> if this instance can be convert to the specified target type; otherwise, <c>false</c>. | |
/// </returns> | |
public static bool CanConvertTo<T>(this object value) | |
{ | |
if (value != null) | |
{ | |
Type targetType = typeof(T); | |
TypeConverter converter = TypeDescriptor.GetConverter(value); | |
if (converter != null) | |
{ | |
if (converter.CanConvertTo(targetType)) | |
{ | |
return true; | |
} | |
} | |
converter = TypeDescriptor.GetConverter(targetType); | |
if (converter != null) | |
{ | |
if (converter.CanConvertFrom(value.GetType())) | |
{ | |
return true; | |
} | |
} | |
} | |
return false; | |
} | |
/// <summary> | |
/// Gets the first matching attribute defined on the data type. | |
/// </summary> | |
/// <typeparam name = "T">The attribute type tp look for.</typeparam> | |
/// <param name = "value">The object to look on.</param> | |
/// <returns>The found attribute</returns> | |
public static T GetAttribute<T>(this object value) where T : Attribute | |
{ | |
return GetAttribute<T>(value, true); | |
} | |
/// <summary> | |
/// Gets the first matching attribute defined on the data type. | |
/// </summary> | |
/// <typeparam name = "T">The attribute type tp look for.</typeparam> | |
/// <param name = "value">The object to look on.</param> | |
/// <param name = "includeInherited">if set to <c>true</c> includes inherited attributes.</param> | |
/// <returns>The found attribute</returns> | |
public static T GetAttribute<T>(this object value, bool includeInherited) where T : Attribute | |
{ | |
if (value == null) | |
throw new ArgumentNullException("value"); | |
ICustomAttributeProvider attrPovider = (value as ICustomAttributeProvider ?? value.GetType()); | |
object[] attributes = attrPovider.GetCustomAttributes(typeof(T), includeInherited); | |
if ((attributes.Length > 0)) | |
{ | |
return (attributes[0] as T); | |
} | |
return null; | |
} | |
/// <summary> | |
/// Reverse of LINQ "Contains" method (analogous to SQL's IN keyword). | |
/// </summary> | |
public static bool In<T>(this T needle, params T[] haystack) | |
{ | |
return haystack.Contains(needle); | |
} | |
/// <summary> | |
/// Reverse of LINQ "Contains" method (analogous to SQL's IN keyword). | |
/// </summary> | |
public static bool In<T>(this T needle, IEnumerable<T> haystack) | |
{ | |
return haystack.Contains(needle); | |
} | |
/// <summary> | |
/// Provides null-checking for member access. If any part of a member access expression is null, the whole expression | |
/// will be lifted to null instead of throwing a NullReferenceException. | |
/// Usage: Use myObj.Lift(n => n.Foo.Bar.Baz.Value) in place of myObj.Foo.Bar.Baz.Value | |
/// </summary> | |
/// <typeparam name="T">Any type</typeparam> | |
/// <typeparam name="TResult">Any nullable type</typeparam> | |
/// <param name="t">Any object</param> | |
/// <param name="expression">An expression involving member access, such as n => n.Foo.Bar.Baz.Value</param> | |
/// <returns>expression.Compile(t), or null if that would have thrown a NullReferenceException.</returns> | |
/// <see cref="http://www.feedharvest.com/item/read/2026586"/> | |
public static TResult Lift<T, TResult>(this T t, Expression<Func<T, TResult>> expression) | |
{ | |
//TODO: ExpressionVisitor is choking on System.Nullable<> instances. Using this until a workaround turns up. | |
try { return expression.Compile()(t); } | |
catch (NullReferenceException) { return default(TResult); } | |
//var nullLiftModifier = new NullLiftModifier(); | |
//var modifiedExpression = (Expression<Func<T, TResult>>)nullLiftModifier.Modify(expression); | |
//return modifiedExpression.Compile()(t); | |
} | |
/// <summary> | |
/// Helper for lifting member access | |
/// </summary> | |
class NullLiftModifier : ExpressionVisitor | |
{ | |
// The method that kicks off the tree walk is protected, so we need to define a public method that provides access to it. | |
public Expression Modify(Expression expression) | |
{ | |
return Visit(expression); | |
} | |
// Change how '.' performs member access. | |
protected override Expression VisitMember(MemberExpression originalExpression) | |
{ | |
var memberAccessExpression = (MemberExpression)base.VisitMember(originalExpression); | |
Expression nullTest = Expression.Equal( | |
memberAccessExpression.Expression, | |
Expression.Constant(null, memberAccessExpression.Expression.Type)); | |
return Expression.Condition( | |
nullTest, | |
Expression.Constant(null, memberAccessExpression.Type), | |
memberAccessExpression); | |
} | |
} | |
} | |
public static class MembersOf<TObj> | |
{ | |
public static string GetName<TProp>(Expression<Func<TObj, TProp>> expression) | |
{ | |
return ObjectExtensions.GetMemberNameImpl(expression.Body); | |
} | |
public static DisplayAttribute GetDisplayAttribute<TProp>(Expression<Func<TObj, TProp>> expression) | |
{ | |
var displayAttr = GetAttribute<TProp, DisplayAttribute>(expression); | |
return displayAttr ?? GetMetadataAttribute<TProp, DisplayAttribute>(expression); | |
} | |
private static TAttr GetMetadataAttribute<TProp, TAttr>(Expression<Func<TObj, TProp>> expression) | |
where TAttr : Attribute | |
{ | |
if (expression.Body is MemberExpression) | |
{ | |
var bodyMemberExpression = expression.Body as MemberExpression; | |
var metadataTypeAttr = bodyMemberExpression.Member.DeclaringType.GetAttribute<MetadataTypeAttribute>(false); | |
if (metadataTypeAttr == null) | |
{ | |
return null; | |
} | |
var propertyInfo = metadataTypeAttr.MetadataClassType.GetProperty(bodyMemberExpression.Member.Name); | |
if (propertyInfo == null) | |
{ | |
return null; | |
} | |
return propertyInfo.GetAttribute<TAttr>(false); | |
} | |
throw new InvalidOperationException("Expression body must be of type MemberExpression."); | |
} | |
public static string GetDisplayName<TProp>(Expression<Func<TObj, TProp>> expression) | |
{ | |
var displayNameAttr = GetDisplayAttribute(expression); | |
return displayNameAttr != null ? displayNameAttr.GetName() : SplitPascalCase(GetName(expression)); | |
} | |
private static string SplitPascalCase(string input) | |
{ | |
if (string.IsNullOrEmpty(input)) | |
{ | |
return input; | |
} | |
return Regex.Replace(input, "([A-Z])", " $1", RegexOptions.Compiled).Trim(); | |
} | |
public static TAttr GetAttribute<TProp, TAttr>(Expression<Func<TObj, TProp>> expression) | |
where TAttr : Attribute | |
{ | |
var body = expression.Body as MemberExpression; | |
if (body != null) | |
{ | |
return body.Member.GetAttribute<TAttr>(false); | |
} | |
throw new InvalidOperationException("Expression body must be of type MemberExpression."); | |
} | |
public static bool HasAttribute<TProp, TAttr>(Expression<Func<TObj, TProp>> expression) | |
where TAttr : Attribute | |
{ | |
return GetAttribute<TProp, TAttr>(expression) != null; | |
} | |
} | |
} |
Sign up for free
to join this conversation on GitHub.
Already have an account?
Sign in to comment