Lambda表达式和扩展方法

时间:2013-09-13 01:55:55

标签: c# linq lambda extension-methods dynamic-linq

所以我有一个像这样定义的扩展方法

  public static String FormatString(this String source, String formatString, Object[] parameters)
  {
               return String.Format(formatString, parameters);
  }

从调查开始,我已经了解到,当调用上面的方法时,它需要3个参数而不是2个,即使在代码中我会在String实例上调用它,如此

String StringInstance = "MYSTRINGINSTANCE";
String StringFormatExpression = "it.FormatString(\"ui{0:D6}\", @0 )";
**  var ,args = new[] {"1", "Another", "YetAnother"}; <-- an array **
//StringInstance.FormatString(StringFormatExpression ,args );

尝试创建一个lambda表示形式,我有以下


return Expression.Call(type, 
                       method.Name, 
                       args.Select(x=>x.GetType()).ToArray<Type>(),
                       args);

当我调用它时,我收到以下错误消息。

类型'System.String'上没有方法'FormatString'。

以下是方法签名。

- {System.String FormatString(System.String,System.String,System.Object [])} System.Reflection.MethodBase {System.Reflection.RuntimeMethodInfo}

不知怎的,它仍然无法找到我的Extension方法来调用。

当我指定定义扩展方法的静态类,并将其作为第一个参数传递给Expression.Call调用时,如此,

更新

:一种。

如上所述,这里有更多代码供您查看。

所以我创建了扩展方法,为我简化了与文件相关的处理,就像这样。

public static IEnumerable ForEach(此IEnumerable源,字符串表达式,params对象[]值)     {          if(source == null)抛出新的ArgumentNullException(“source”);          if(expression == null)抛出新的ArgumentNullException(“selector”);          var enumerableList = source.AsEnumerable();

     IEnumerable<T> finalList = from T item in source
                     select (T) DynamicLambdaExpression.ParseLambda(item.GetType(), typeof(T), expression, values).Compile().DynamicInvoke(item);

     return finalList;
}

public static IEnumerable ForEachFileName(此IEnumerable sourceFilePaths,string expression,object [] values)       {            if(sourceFilePaths == null)抛出新的ArgumentNullException(“source”);            IDictionary source = sourceFilePaths.Select((value,index)=&gt;            {value = Path.GetFileNameWithoutExtension(value); return new {index,value}; }         ).ToDictionary(x =&gt; x.index,v =&gt; v.value);            if(expression == null)抛出新的ArgumentNullException(“selector”);

       IEnumerable<String> finalList = from int index in source.Keys
                                       select (String)DynamicLambdaExpression.ParseLambda(source[index].GetType(), typeof(String), expression, values).Compile().DynamicInvoke(source[index]);

       return finalList;
  }

然后在我的测试中,我有以下

String StringFormatExpression = "it.FormatString(\"ui{0:D6}\", @0 )";
var param = new[] {"1", "Another", "YetAnother"};

String result = new[] { "MyStringValue", "MySecondStringValue", "MyThirdString"}.ForEachFileName(StringFormatExpression, param).FirstOrDefault();

 Console.WriteLine(result);

℃。由于DynamicQuery库无法找到我之前为我的字符串定义的扩展方法,因此我修改了代码以捕获扩展方法。

        internal partial class ExpressionParser
 {
      Expression ParseMemberAccess(Type type, Expression instance)
      {
           if (instance != null) type = instance.Type;
           int errorPos = token.pos;
           string id = GetIdentifier();
           NextToken();
           if (token.id == TokenId.OpenParen)
           {
                if (instance != null && type != typeof(string))
                {
                     Type enumerableType = FindGenericType(typeof(IEnumerable<>), type);
                     if (enumerableType != null)
                     {
                          Type elementType = enumerableType.GetGenericArguments()[0];
                          return ParseAggregate(instance, elementType, id, errorPos);
                     }
                }
                Expression[] args = ParseArgumentList();
                MethodBase mb;

                switch (FindMethod(type, id, instance == null,instance, args,  out mb))
                {
                     case 0:
                          throw ParseError(errorPos, Res.NoApplicableMethod,
                              id, GetTypeName(type));
                     case 1:
                          MethodInfo method = (MethodInfo)mb;
                          if ((method.DeclaringType.IsAbstract && method.DeclaringType.IsSealed))
                          {
                               if (method.ReturnType == typeof(void))
                                    throw ParseError(errorPos, Res.MethodIsVoid,
                                        id, GetTypeName(method.DeclaringType));

                               Type t = mb.ReflectedType;
                               var combined = instance.Concatenate(args);
                               return Expression.Call(method, combined);
                          }

                          else if (IsPredefinedType(method.DeclaringType))
                          {
                               if (method.ReturnType == typeof(void))
                                    throw ParseError(errorPos, Res.MethodIsVoid,
                                        id, GetTypeName(method.DeclaringType));
                               return Expression.Call(instance, (MethodInfo)method, args);
                          }
                          else
                          {

                               throw ParseError(errorPos, Res.MethodsAreInaccessible, GetTypeName(method.DeclaringType));

                          }
                     default:
                          throw ParseError(errorPos, Res.AmbiguousMethodInvocation,
                              id, GetTypeName(type));
                }//end of switch method here 
           }
           else
           {
                MemberInfo member = FindPropertyOrField(type, id, instance == null);
                if (member == null)
                     throw ParseError(errorPos, Res.UnknownPropertyOrField,
                         id, GetTypeName(type));
                return member is PropertyInfo ?
                    Expression.Property(instance, (PropertyInfo)member) :
                    Expression.Field(instance, (FieldInfo)member);
           }
      }


      /// <summary>
      /// Comment Added 9/13/2013
      /// An Extension method would require that the instance be the first argument in it's parameter list
      /// </summary>
      /// <param name="type"></param>
      /// <param name="methodName"></param>
      /// <param name="staticAccess"></param>
      /// <param name="args"></param>
      /// <param name="method"></param>
      /// <returns></returns>
      int FindMethod(Type type, string methodName, bool staticAccess, Expression instance, Expression[] args, out MethodBase method)
      {
           if (type.IsGenericParameter)
           {
                BindingFlags flags = BindingFlags.Public | BindingFlags.DeclaredOnly |
                    (staticAccess ? BindingFlags.Static : BindingFlags.Instance);

                foreach (Type t in SelfAndBaseTypes(type))
                {
                     MemberInfo[] members = t.FindMembers(MemberTypes.Method,
                         flags, Type.FilterNameIgnoreCase, methodName);
                     int count = FindBestMethod(members.Cast<MethodBase>(), instance, args, out method);
                     if (count != 0) return count;
                }
           }
           else
           {
                IEnumerable<Type> selfAndBaseTypes = SelfAndBaseTypes(type);

                foreach (Type t in selfAndBaseTypes)
                {
                     List<MethodInfo> methodinfos = new List<MethodInfo>();
                     methodinfos.AddRange(t.GetMethods(BindingFlags.Public | BindingFlags.Instance | BindingFlags.DeclaredOnly));
                     //also add extension methods
                     methodinfos.AddRange(t.GetExtensionMethods());
                     int count = FindBestMethod(methodinfos.Cast<MethodBase>(), instance, args, out method);
                     if (count != 0) return count;
                }


           }
           method = null;
           return 0;
      }

      /// <summary>
      /// Comment Added 9/13/2013
      /// An Extension method would require that the instance be the first argument in it's parameter list
      /// </summary>
      /// <param name="type"></param>
      /// <param name="methodName"></param>
      /// <param name="staticAccess"></param>
      /// <param name="args"></param>
      /// <param name="method"></param>
      /// <returns></returns>
      int FindMethod(Type type, string methodName, bool staticAccess, Expression[] args, out MethodBase method)
      {
           if (type.IsGenericParameter)
           {
                BindingFlags flags = BindingFlags.Public | BindingFlags.DeclaredOnly |
                    (staticAccess ? BindingFlags.Static : BindingFlags.Instance);

                foreach (Type t in SelfAndBaseTypes(type))
                {
                     MemberInfo[] members = t.FindMembers(MemberTypes.Method,
                         flags, Type.FilterNameIgnoreCase, methodName);
                     int count = FindBestMethod(members.Cast<MethodBase>(), args, out method);
                     if (count != 0) return count;
                }
           }
           else
           {
                IEnumerable<Type> selfAndBaseTypes = SelfAndBaseTypes(type);

                foreach (Type t in selfAndBaseTypes)
                {
                     List<MethodInfo> methodinfos = new List<MethodInfo>();
                     methodinfos.AddRange(t.GetMethods(BindingFlags.Public | BindingFlags.Instance | BindingFlags.DeclaredOnly));
                     //also add extension methods
                     methodinfos.AddRange(t.GetExtensionMethods());
                     int count = FindBestMethod(methodinfos.Cast<MethodBase>(), args, out method);
                     if (count != 0) return count;
                }


           }
           method = null;
           return 0;
      }

      int FindBestMethod(IEnumerable<MethodBase> methods, Expression[] args, out MethodBase method)
      {
           //search for the best base method 
           int length = FindBestInstanceMethod(methods, args, out method);

           return length;
      }

      int FindBestMethod(IEnumerable<MethodBase> methods, Expression instance, Expression[] args, out MethodBase method)
      {
            //search for the best base method 
               int length = FindBestInstanceMethod(methods, args, out method);
               //in the case that no best method is found that way, try a search for Extension methods
               if(length == 0)
                    length = FindBestExtensionMethod(methods, instance, args, out method);
               return length;
      }

      private int FindBestExtensionMethod(IEnumerable<MethodBase> methods, Expression instance, Expression[] args, out MethodBase method)
      {
           MethodData[] applicable = methods.
                 Select(m => new MethodData { MethodBase = m, Parameters = m.GetParameters() }).
                 Where(m => m.MethodBase.IsDefined(typeof(ExtensionAttribute), false) 
                      && IsApplicableExtensionMethod(m, instance, args)).
                 ToArray();
           if (applicable.Length > 1)
           {
                applicable = applicable.
                    Where(m => applicable.All(n => m == n || IsBetterThan(args, m, n))).
                    ToArray();
           }
           if (applicable.Length == 1)
           {
                MethodData md = applicable[0];

                for (int i = 0; i < args.Length; i++) args[i] = md.Args[i];
                method = md.MethodBase;
           }
           else
           {
                method = null;
           }
           return applicable.Length;
      }

      private int FindBestInstanceMethod(IEnumerable<MethodBase> methods, Expression[] args, out MethodBase method)
      {
           MethodData[] applicable = methods.
              Select(m => new MethodData { MethodBase = m, Parameters = m.GetParameters() }).
              Where(m => IsApplicable(m, args)).
              ToArray();

           if (applicable.Length > 1)
           {
                applicable = applicable.
                    Where(m => applicable.All(n => m == n || IsBetterThan(args, m, n))).
                    ToArray();
           }
           if (applicable.Length == 1)
           {
                MethodData md = applicable[0];
                for (int i = 0; i < args.Length; i++) args[i] = md.Args[i];
                method = md.MethodBase;
           }
           else
           {
                method = null;
           }
           return applicable.Length;
      }


      bool IsApplicableExtensionMethod(MethodData method, Expression instance, Expression[] args)
      {
           if ((method.Parameters.Length - 1) != (args.Length)) return false;

           var argsource = instance.Concatenate(args);
           Expression[] promotedArgs = new Expression[argsource.Length];
           for (int i = 0; i < (argsource.Length); i++)
           {   
                ParameterInfo pi = method.Parameters[i];
                if (pi.IsOut) return false;
                Expression promoted = PromoteExpression(argsource[i], pi.ParameterType, false);
                if (promoted == null) return false;
                promotedArgs[i] = argsource[i];
           }
           method.Args = promotedArgs;
           return true;
      }


      bool IsApplicable(MethodData method, Expression[] args)
      {
           if (method.Parameters.Length != args.Length) return false;
           Expression[] promotedArgs = new Expression[args.Length];
           for (int i = 0; i < args.Length; i++)
           {
                ParameterInfo pi = method.Parameters[i];
                if (pi.IsOut) return false;
                Expression promoted = PromoteExpression(args[i], pi.ParameterType, false);
                if (promoted == null) return false;
                promotedArgs[i] = promoted;
           }
           method.Args = promotedArgs;
           return true;
      }

      Expression PromoteExpression(Expression expr, Type type, bool exact)
      {
           if (expr.Type == type) return expr;
           if (expr is ConstantExpression)
           {
                ConstantExpression ce = (ConstantExpression)expr;
                if (ce == nullLiteral)
                {
                     if (!type.IsValueType || IsNullableType(type))
                          return Expression.Constant(null, type);
                }
                else
                {
                     string text;
                     if (literals.TryGetValue(ce, out text))
                     {
                          Type target = GetNonNullableType(type);
                          Object value = null;
                          switch (Type.GetTypeCode(ce.Type))
                          {
                               case TypeCode.Int32:
                               case TypeCode.UInt32:
                               case TypeCode.Int64:
                               case TypeCode.UInt64:
                                    value = ParseNumber(text, target);
                                    break;
                               case TypeCode.Double:
                                    if (target == typeof(decimal)) value = ParseNumber(text, target);
                                    break;
                               case TypeCode.String:
                                    value = ParseEnum(text, target);
                                    break;
                          }
                          if (value != null)
                               return Expression.Constant(value, type);
                     }
                }
           }
           if (IsCompatibleWith(expr.Type, type))
           {
                if (type.IsValueType || exact) return Expression.Convert(expr, type);
                return expr;
           }
           return null;
      }

 }

d。当我运行上面的操作时,我收到以下错误消息

类型'System.String'上没有方法'FormatString'。

1 个答案:

答案 0 :(得分:1)

扩展方法不会向类型的定义添加方法。它们只是静态方法调用的语法糖。您为Type的包含类型传入的FormatStrign适用于string,但适用于定义FormatString的任何类型如果您在名为StringExtensions的类中定义了它,那么 是您需要传入的Type