Click here to Skip to main content
15,896,448 members
Articles / Programming Languages / C#

Introducing the LinFu Framework, Part II: LinFu.DynamicObject – Adding Dynamic Language Features to Statically Typed Languages

Rate me:
Please Sign up or sign in to vote.
4.97/5 (50 votes)
12 Nov 2007LGPL316 min read 145.1K   882   67  
Using LinFu.DynamicObject to add mixins, duck typing and multiple dispatch to your favorite .NET languages
using System;
using System.Collections.Generic;
using System.Reflection;
using System.Text;

namespace LinFu.Reflection
{
    public class PredicateBuilder
    {
        private bool _matchCovariantReturnType;
        private bool _matchCovariantParameterTypes;
        private bool _matchRuntimeArguments;
        private bool? _isPublic;
        private bool? _isProtected;
        private string _methodName;
        private Type _returnType;
        private readonly List<Type> _typeArguments = new List<Type>();
        private readonly List<ParameterInfo> _parameterTypes = new List<ParameterInfo>();
        private readonly List<object> _arguments = new List<object>();
        private bool _matchParameters = true;

        public List<object> RuntimeArguments
        {
            get { return _arguments; }
        }
        public string MethodName
        {
            get { return _methodName; }
            set { _methodName = value; }
        }

        public Type ReturnType
        {
            get { return _returnType; }
            set { _returnType = value; }
        }

        public List<ParameterInfo> ParameterTypes
        {
            get { return _parameterTypes; }
        }

        public List<Type> TypeArguments
        {
            get { return _typeArguments; }
        }

        public bool? IsPublic
        {
            get { return _isPublic; }
            set { _isPublic = value; }
        }

        public bool? IsProtected
        {
            get { return _isProtected; }
            set { _isProtected = value; }
        }

        public bool MatchCovariantReturnType
        {
            get { return _matchCovariantReturnType; }
            set { _matchCovariantReturnType = value; }
        }

        public bool MatchParameters
        {
            get { return _matchParameters; }
            set { _matchParameters = value; }
        }

        public bool MatchCovariantParameterTypes
        {
            get { return _matchCovariantParameterTypes; }
            set { _matchCovariantParameterTypes = value; }
        }

        public bool MatchRuntimeArguments
        {
            get { return _matchRuntimeArguments; }
            set { _matchRuntimeArguments = value; }
        }
        public static Predicate<MethodInfo> CreatePredicate(MethodInfo method)
        {
            PredicateBuilder builder = new PredicateBuilder();
            builder.MatchParameters = true;
            builder.MethodName = method.Name;

            foreach (ParameterInfo param in method.GetParameters())
            {
                builder.ParameterTypes.Add(param);
            }

            // Match any type arguments
            if (method.IsGenericMethod)
            {
                foreach (Type current in method.GetGenericArguments())
                {
                    builder.TypeArguments.Add(current);
                }
            }
            builder.ReturnType = method.ReturnType;

            Predicate<MethodInfo> predicate = builder.CreatePredicate();

            return predicate;
        }
        public Predicate<MethodInfo> CreatePredicate()
        {
            Predicate<MethodInfo> result = null;

            #region Match the method name
            if (!string.IsNullOrEmpty(_methodName))
            {
                Predicate<MethodInfo> shouldMatchMethodName =
                    delegate(MethodInfo method)
                    {
                        return method.Name == _methodName;
                    };

                // Results that match the method name will get a higher
                // score
                result += shouldMatchMethodName;
                result += shouldMatchMethodName;
            }
            #endregion

            #region Match the return type
            if (_returnType != null)
            {
                result += delegate(MethodInfo method)
                              {
                                  return method.ReturnType == _returnType;
                              };

                if (_matchCovariantReturnType)
                {
                    result += delegate(MethodInfo method)
                              {
                                  return method.ReturnType.IsAssignableFrom(_returnType);
                              };
                }
            }
            #endregion


            #region Match the parameters
            if (_matchParameters && _parameterTypes.Count > 0)
            {
                Predicate<MethodInfo> hasParameterTypes = null;
                ParameterInfo[] currentParameters = _parameterTypes.ToArray();

                // Match the parameter count
                int parameterCount = currentParameters != null ? currentParameters.Length : 0;

                result += delegate(MethodInfo method)
                              {
                                  ParameterInfo[] parameters = method.GetParameters();
                                  int count = parameters == null ? 0 : parameters.Length;

                                  return parameterCount == count;
                              };

                // Match the parameter types
                foreach (ParameterInfo param in currentParameters)
                {
                    int position = param.Position;
                    Type parameterType = param.ParameterType;
                    Predicate<MethodInfo> hasParameter = MakeParameterPredicate(position, parameterType, _matchCovariantParameterTypes);

                    hasParameterTypes += hasParameter;
                }

                if (hasParameterTypes != null)
                    result += hasParameterTypes;
            }


            // Check for zero parameters
            if (_matchParameters && _parameterTypes.Count == 0)
            {
                result += delegate(MethodInfo currentMethod)
                              {
                                  ParameterInfo[] currentParameters = currentMethod.GetParameters();

                                  // Match the parameter count
                                  int parameterCount = currentParameters != null ? currentParameters.Length : 0;
                                  return parameterCount == 0;
                              };
            }
            #endregion

            #region Match the generic type parameters
            if (_typeArguments.Count > 0)
            {
                Predicate<MethodInfo> matchesTypeParameters = null;
                int position = 0;
                foreach (Type currentType in _typeArguments)
                {
                    matchesTypeParameters += delegate(MethodInfo method)
                                                 {
                                                     if (!method.IsGenericMethod)
                                                         return false;

                                                     Type[] typeArgs = method.GetGenericArguments();
                                                     bool isMatch = false;

                                                     try
                                                     {
                                                         isMatch = typeArgs[position++] == currentType;
                                                     }
                                                     catch
                                                     {
                                                         // Ignore the error 
                                                     }
                                                     return isMatch;
                                                 };
                }

                result += matchesTypeParameters;
            }

            if (_typeArguments.Count == 0)
            {
                result += delegate(MethodInfo currentMethod)
                              {
                                  Type[] currentParameterTypes = currentMethod.GetGenericArguments();

                                  // Match the Type parameter
                                  int parameterCount = currentParameterTypes != null ? currentParameterTypes.Length : 0;
                                  return parameterCount == 0;
                              };
            }
            #endregion

            #region Match public methods
            if (_isPublic != null && _isPublic.HasValue)
            {
                result += delegate(MethodInfo method)
                              {
                                  return method.IsPublic == _isPublic;
                              };
            }
            #endregion

            #region Match protected methods
            if (_isProtected != null && _isProtected.HasValue)
            {
                result += delegate(MethodInfo method)
                              {
                                  return method.IsFamily == _isProtected;
                              };
            }
            #endregion

            #region Match the runtime arguments
            if (_arguments.Count > 0 && MatchRuntimeArguments)
            {
                int position = 0;
                foreach (object argument in _arguments)
                {
                    if (argument != null)
                    {
                        Type argumentType = argument.GetType();
                        result += MakeParameterPredicate(position, argumentType, _matchCovariantParameterTypes);
                    }
                    position++;
                }
            }

            if (_arguments.Count == 0 && MatchRuntimeArguments)
            {
                result += delegate(MethodInfo currentMethod)
                              {
                                  ParameterInfo[] currentParameters = currentMethod.GetParameters();

                                  // Match the parameter count
                                  int parameterCount = currentParameters != null ? currentParameters.Length : 0;
                                  return parameterCount == 0;
                              };
            }
            #endregion
            return result;
        }

        private static Predicate<MethodInfo>
            MakeParameterPredicate(int position, Type parameterType, bool covariant)
        {

            Predicate<MethodInfo> result = delegate(MethodInfo method)
                       {
                           ParameterInfo[] parameters = method.GetParameters();

                           bool checkResult = false;
                           try
                           {
                               if (!covariant)
                                   checkResult = parameters[position].ParameterType == parameterType;

                               if (covariant)
                                   checkResult = parameters[position].ParameterType.IsAssignableFrom(parameterType);
                           }
                           catch
                           {
                               // Ignore any errors that occur
                           }
                           return checkResult;
                       };

            return result;
        }

        public void SetParameterTypes(ParameterInfo[] parameterInfo)
        {
            if (parameterInfo != null && parameterInfo.Length > 0)
                _parameterTypes.AddRange(parameterInfo);
        }
    }
}

By viewing downloads associated with this article you agree to the Terms of Service and the article's licence.

If a file you wish to view isn't highlighted, and is a text file (not binary), please let us know and we'll add colourisation support for it.

License

This article, along with any associated source code and files, is licensed under The GNU Lesser General Public License (LGPLv3)


Written By
Software Developer (Senior) Readify
Australia Australia
This member has not yet provided a Biography. Assume it's interesting and varied, and probably something to do with programming.

Comments and Discussions