Skip to main content

Extension methods for the C# object class.

using System;
using System.Text;
using System.Linq;
using System.Reflection;
using System.Collections.Generic;

public static class ObjectExtensions
{
    /// <summary>
    /// Uses reflection to output a string dump of all the object's properties
    /// and values in a nice readable format. Should only be used for
    /// debug/error dumps as performance of reflection is obviously a concern.
    /// </summary>
    /// <param name="instance">Object instance to dump info for.</param>
    /// <returns>Formatted friendly string of object information.</returns>
    public static string ToStringDump(this object instance)
    {
        var flags = System.Reflection.BindingFlags.Instance | System.Reflection.BindingFlags.Public | System.Reflection.BindingFlags.FlattenHierarchy;
        var propInfos = instance.GetType().GetProperties(flags);

        var sb = new StringBuilder();

        string typeName = instance.GetType().Name;
        sb.AppendLine(typeName);
        sb.AppendLine("".PadRight(typeName.Length + 5, '='));

        foreach (System.Reflection.PropertyInfo propInfo in propInfos)
        {
            sb.Append(propInfo.Name);
            sb.Append(": ");
            if (propInfo.GetIndexParameters().Length > 0)
            {
                sb.Append("Indexed Property cannot be used");
            }
            else
            {
                object value = propInfo.GetValue(instance, null);
                sb.Append(value != null ? value : "null");
            }
            sb.Append(System.Environment.NewLine);
        }

        return sb.ToString();
    }

    /// <remarks>https://github.com/madhatter22/LinqToLdap/blob/master/LinqToLdap.Tests/TestSupport/ExtensionMethods/ObjectExtensions.cs</remarks>
    private const BindingFlags Flags = BindingFlags.Public | BindingFlags.NonPublic | BindingFlags.Instance | BindingFlags.FlattenHierarchy;

    /// <remarks>https://github.com/madhatter22/LinqToLdap/blob/master/LinqToLdap.Tests/TestSupport/ExtensionMethods/ObjectExtensions.cs</remarks>
    public static T As<T>(this object obj) where T : class
    {
        return obj as T;
    }

    /// <remarks>https://github.com/madhatter22/LinqToLdap/blob/master/LinqToLdap.Tests/TestSupport/ExtensionMethods/ObjectExtensions.cs</remarks>
    public static T CastTo<T>(this object obj)
    {
        return (T)obj;
    }

    /// <remarks>https://github.com/madhatter22/LinqToLdap/blob/master/LinqToLdap.Tests/TestSupport/ExtensionMethods/ObjectExtensions.cs</remarks>
    public static object Call(this object obj, string methodName, params object[] parameters)
    {
        var type = obj.GetType();
        var method = type.GetMethod(methodName, Flags, null, parameters == null ? Type.EmptyTypes : parameters.Select(p => p.GetType()).ToArray(), null);

        return method.Invoke(obj, parameters);
    }

    /// <summary>
    /// Returns a field given the name and the type of object.
    /// </summary>
    /// <param name="fieldName">The name of the field</param>
    /// <param name="objectType">The type of object to which the field belongs</param>
    /// <returns>The field</returns>
    /// <remarks>https://github.com/madhatter22/LinqToLdap/blob/master/LinqToLdap.Tests/TestSupport/ExtensionMethods/ObjectExtensions.cs</remarks>
    private static FieldInfo GetField(string fieldName, Type objectType)
    {
        FieldInfo field = objectType.GetField(fieldName, Flags);
        if (field == null)
        {
            Type baseType = objectType.BaseType;
            while (baseType != typeof(object))
            {
                field = GetField(fieldName, baseType);
                if (field != null)
                {
                    break;
                }
                baseType = baseType.BaseType;
            }
        }
        return field;
    }

    /// <summary>
    /// Returns a property given the name and the type of object.
    /// </summary>
    /// <param name="propertyName">The name of the property</param>
    /// <param name="objectType">The type of object to which the property belongs</param>
    /// <returns>The property</returns>
    /// <remarks>https://github.com/madhatter22/LinqToLdap/blob/master/LinqToLdap.Tests/TestSupport/ExtensionMethods/ObjectExtensions.cs</remarks>
    private static PropertyInfo GetProperty(string propertyName, Type objectType)
    {
        PropertyInfo property = objectType.GetProperty(propertyName, Flags);
        if (property == null)
        {
            Type baseType = objectType.BaseType;
            while (baseType != typeof(object))
            {
                property = GetProperty(propertyName, baseType);
                if (property != null)
                {
                    break;
                }
                baseType = baseType.BaseType;
            }
        }
        return property;
    }

    /// <summary>
    /// Gets the value of a field for an object.
    /// </summary>
    /// <typeparam name="TProperty">The type of the field to look for.</typeparam>
    /// <param name="propertyName">The name of the field.</param>
    /// <param name="source">>The object to which the field belongs</param>
    /// <returns>The value of the field</returns>
    /// <remarks>https://github.com/madhatter22/LinqToLdap/blob/master/LinqToLdap.Tests/TestSupport/ExtensionMethods/ObjectExtensions.cs</remarks>
    public static TProperty PropertyValue<TProperty>(this object source, string propertyName)
    {
        PropertyInfo property = GetProperty(propertyName, source.GetType());

        return (TProperty)property.GetValue(source, Flags, null, null, null);
    }

    /// <summary>
    /// Sets the value of a property for an object.
    /// </summary>
    /// <typeparam name="TProperty">The type of the property to look for.</typeparam>
    /// <param name="propertyName">The name of the property.</param>
    /// <param name="source">>The object to which the property belongs</param>
    /// <param name="value">The value to which the property will be set.</param>
    /// <remarks>https://github.com/madhatter22/LinqToLdap/blob/master/LinqToLdap.Tests/TestSupport/ExtensionMethods/ObjectExtensions.cs</remarks>
    public static void SetPropertyValue<TProperty>(this object source, string propertyName, TProperty value)
    {
        PropertyInfo property = GetProperty(propertyName, source.GetType());
        property.SetValue(source, value, Flags, null, null, null);
    }

    /// <summary>
    /// Gets the value of a field for an object.
    /// </summary>
    /// <typeparam name="TField">The type of the field to look for.</typeparam>
    /// <param name="fieldName">The name of the field.</param>
    /// <param name="source">>The object to which the field belongs</param>
    /// <returns>The value of the field</returns>
    /// <remarks>https://github.com/madhatter22/LinqToLdap/blob/master/LinqToLdap.Tests/TestSupport/ExtensionMethods/ObjectExtensions.cs</remarks>
    public static TField FieldValueEx<TField>(this object source, string fieldName)
    {
        FieldInfo field = GetField(fieldName, source.GetType());

        return (TField)field.GetValue(source);
    }

    /// <summary>
    /// Gets the value of a field for an object.
    /// </summary>
    /// <typeparam name="TField">The type of the field to look for.</typeparam>
    /// <param name="fieldName">The name of the field.</param>
    /// <param name="source">>The object to which the field belongs</param>
    /// <param name="value">The value to which the field will be set.</param>
    /// <remarks>https://github.com/madhatter22/LinqToLdap/blob/master/LinqToLdap.Tests/TestSupport/ExtensionMethods/ObjectExtensions.cs</remarks>
    public static void SetFieldValue<TField>(this object source, string fieldName, TField value)
    {
        FieldInfo field = GetField(fieldName, source.GetType());

        field.SetValue(source, value);
    }

    /// <summary>
    /// Transforms the values of the object into a dictionary of strings.
    /// </summary>
    /// <param name="values">The object instance to convert.</param>
    /// <returns>A dictionary mapping field names to values.</returns>
    /// <remarks>https://github.com/AngleSharp/AngleSharp/blob/master/src/AngleSharp/Common/ObjectExtensions.cs#L18</remarks>
    public static Dictionary<string, string> ToDictionary(this object values)
    {
        var symbols = new Dictionary<string, string>();
        
        if (values != null)
        {
            var properties = values.GetType().GetProperties();
            foreach (var property in properties)
            {
                var value = property.GetValue(values, null) ?? string.Empty;
                symbols.Add(property.Name, value.ToString());
            }
        }

        return symbols;
    }

    /// <summary>
    /// Tries to obtain the given key, otherwise returns the default value.
    /// </summary>
    /// <typeparam name="T">The struct type.</typeparam>
    /// <param name="values">The dictionary for the lookup.</param>
    /// <param name="key">The key to look for.</param>
    /// <returns>A nullable struct type.</returns>
    /// <remarks>https://github.com/AngleSharp/AngleSharp/blob/master/src/AngleSharp/Common/ObjectExtensions.cs#L106</remarks>
    public static T? TryGet<T>(this IDictionary<string, object> values, string key)
        where T : struct
    {
        if (values.TryGetValue(key, out var value) && value is T result)
        {
            return result;
        }

        return null;
    }

    /// <summary>
    /// Tries to obtain the given key, otherwise returns null.
    /// </summary>
    /// <param name="values">The dictionary for the lookup.</param>
    /// <param name="key">The key to look for.</param>
    /// <returns>An object instance or null.</returns>
    /// <remarks>https://github.com/AngleSharp/AngleSharp/blob/master/src/AngleSharp/Common/ObjectExtensions.cs#L123</remarks>
    public static object TryGet(this IDictionary<string, object> values, string key)
    {
        values.TryGetValue(key, out var value);
        return value;
    }

    /// <summary>
    /// Gets the value of the given key, otherwise the provided default
    /// value.
    /// </summary>
    /// <typeparam name="T">The type of the keys.</typeparam>
    /// <typeparam name="TU">The type of the value.</typeparam>
    /// <param name="values">The dictionary for the lookup.</param>
    /// <param name="key">The key to look for.</param>
    /// <param name="defaultValue">The provided fallback value.</param>
    /// <returns>The value or the provided fallback.</returns>
    /// <remarks>https://github.com/AngleSharp/AngleSharp/blob/master/src/AngleSharp/Common/ObjectExtensions.cs#L139</remarks>
    public static TU GetOrDefault<T, TU>(this IDictionary<T, TU> values, T key, TU defaultValue)
    {
        return values.TryGetValue(key, out var value) ? value : defaultValue;
    }
}