Lambda表达式和扩展方法
所以我有一个像这样定义的扩展方法
public static String FormatString(this String source, String formatString, Object[] parameters)
{
return String.Format(formatString, parameters);
}
从调查,我已经了解到,当上述方法被调用时,它需要3个参数,而不是两个,即使在代码中,我会在像这样的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}
不知何故,它仍然无法找到我的扩展方法调用。
当我确定了定义扩展方法的静态类并将其作为第一个参数传递给Expression.Call调用时,就像这样,
UPDATE
一个。
按照要求,这里有更多的代码供您查看。
所以我创建了扩展方法来简化与我相关的文件相关处理,就像这样。
公共静态IEnumerable ForEach(此IEnumerable源,字符串表达式,params对象[]值){如果(源==空)抛出新的ArgumentNullException(“源”); 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(this IEnumerable sourceFilePaths,string expression,object [] values){if(sourceFilePaths == null)throw new ArgumentNullException(“source”); IDictionary source = sourceFilePaths.Select((value,index)=> {value = Path.GetFileNameWithoutExtension(value); return new {index,value};}).ToDictionary(x => x.index,v => 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;
}
B.
然后在我的测试中,我有以下几点
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);
C.由于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'方法。
扩展方法不会为类型的定义添加方法。 它们只是静态方法调用的语法糖。 该Type
要传递在的包含类型FormatStrign
是string
,但它应该是什么类型FormatString
被定义的。如果你在一个类中调用它定义StringExtensions
然后就是Type
,你需要通过。