Skip to content

Instantly share code, notes, and snippets.

@gabrieljoelc
Created March 29, 2013 15:35
Show Gist options
  • Save gabrieljoelc/5271577 to your computer and use it in GitHub Desktop.
Save gabrieljoelc/5271577 to your computer and use it in GitHub Desktop.
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