だから私はそのように定義された拡張メソッドを持っています
public static String FormatString(this String source, String formatString, Object[] parameters)
{
return String.Format(formatString, parameters);
}
調査の結果、上記のメソッドが呼び出されると、コードでは String インスタンスで呼び出しても、2 つではなく 3 つのパラメーターが必要になることがわかりました。
String StringInstance = "MYSTRINGINSTANCE";
String StringFormatExpression = "it.FormatString(\"ui{0:D6}\", @0 )";
** var ,args = new[] {"1", "Another", "YetAnother"}; <-- an array **
//StringInstance.FormatString(StringFormatExpression ,args );
これのラムダ表現を作成しようとすると、次のようになります
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 呼び出しの最初の引数として渡すと、次のようになります。
アップデート
A.
リクエストに応じて、ここにコードの詳細を示します。
そこで、ファイル関連の処理を単純化する拡張メソッドを作成しました。
public static IEnumerable ForEach(this IEnumerable source, string expression, params object[] values) { if (source == null) throw new ArgumentNullException("source"); if (expression == null) throw new 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) 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) throw new 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' が存在しません。