static class Example
{
public static string Method<T>(ref List<string> p2, out string p3, string p4)
{
...
}
public static string Method<T>(ref List<string> p2, out string p3, int p4)
{
...
}
}
以下显然不起作用,但这就是想法:
public static string MethodCaller(Type theType, ref List<string> p2, out string p3, string p4)
{
Method<theType>(ref p2, out p3, p4);
}
使用GetMethod?它如何知道使用哪两个重载方法? 我们应该使用Expression.Call吗?我们如何处理ref和out参数?
请帮助:)
答案 0 :(得分:4)
这可以通过反射来完成,虽然找到正确的重载有点乱:
class Program
{
static void Main(string[] args)
{
List<string> p2 = new List<string>();
string p3;
string p4 = "input string";
string result = MethodCaller(typeof(DateTime), ref p2, out p3, p4);
}
public static string MethodCaller(Type theType, ref List<string> p2, out string p3, string p4)
{
MethodInfo method = (from m in typeof(Example).GetMethods()
let p = m.GetParameters()
where m.Name == "Method"
&& p.Length == 3
&& p[0].ParameterType.IsByRef
&& p[0].ParameterType.HasElementType
&& p[0].ParameterType.GetElementType() == typeof(List<string>)
&& p[1].ParameterType.IsByRef
&& p[1].ParameterType.HasElementType
&& p[1].ParameterType.GetElementType() == typeof(string)
&& p[2].ParameterType == typeof(string)
select m).Single();
MethodInfo genericMethod = method.MakeGenericMethod(theType);
object[] parameters = new object[] { null, null, p4 };
string returnValue = (string)genericMethod.Invoke(null, parameters);
p2 = (List<string>)parameters[0];
p3 = (string)parameters[1];
return returnValue;
}
}
static class Example
{
public static string Method<T>(ref List<string> p2, out string p3, string p4)
{
p2 = new List<string>();
p2.Add(typeof(T).FullName);
p2.Add(p4);
p3 = "output string";
return "return value";
}
public static string Method<T>(ref List<string> p2, out string p3, int p4)
{
p2 = new List<string>();
p2.Add(typeof(T).FullName);
p2.Add(p4.ToString());
p3 = "output string";
return "return value";
}
}
答案 1 :(得分:0)
我一直在寻找类似的方法,但很遗憾没有找到它 - 但后来决定自己解决它。但是 - 在原型制作过程中,我发现了&#39; out&#39;和&#39; ref&#39;是互斥的 - 所以你只能支持其中一个。
所以我想支持如下语法:
object DoCall<A1>( String function, A1 a1 )
这需要生成如下函数:
object DoCall<A1>( String function, out A1 a1 )
object DoCall<A1>( String function, ref A1 a1 )
哪个编译器不喜欢。
所以我决定只支持&#39; ref&#39;关键字 - 因为它可以支持进出方向,但是&#39; out&#39;只支持方向。
但另外有人会注意到 - 如果你需要支持所有类型的参数排列 - 简单的编码是不够的 - 你需要编写代码生成器 - 我最后做了什么。
所以测试代码看起来像这样:
using System;
using System.Collections.Generic;
using System.Diagnostics;
using System.IO;
using System.Linq;
using System.Reflection;
using System.Text;
using System.Text.RegularExpressions;
using System.Threading.Tasks;
namespace TestReflection
{
public class CustomClassAsArg
{
public string MyInfo { get; set; }
}
public class CallMe
{
public void Hello1(String msg, int i)
{
Console.WriteLine(msg + ": " + i.ToString());
}
public void Hello2(ref String msg)
{
msg += "out string";
}
public void Hello2(ref int a)
{
a += 2;
}
public string Hello3(string a)
{
return a + "--";
}
public static bool MyStaticMethod( int arg, ref String outs )
{
outs = "->" + arg.ToString();
return true;
}
public bool? ThreeStateTest( int i )
{
switch ( i )
{
case 0:
return null;
case 1:
return false;
case 2:
return true;
}
return null;
}
public void UpdateCC( CustomClassAsArg c )
{
c.MyInfo = "updated";
}
}
class Program
{
static void Main(string[] args)
{
ClassCaller.UpdateSourceCodeHelperFunctions(2);
CallMe m = new CallMe();
ClassCaller c = new ClassCaller(m);
string r = "in string ";
int arg = 1;
String sx = "";
object ox = c.DoCall("!MyStaticMethod", 23, ref sx);
Console.WriteLine(sx);
c.DoCall("Hello1", "hello world", 1);
c.DoCall("Hello2", ref r);
Console.WriteLine(r);
c.DoCall("Hello2", ref arg);
Console.WriteLine(arg.ToString());
bool? rt = (bool?)c.DoCall("ThreeStateTest", 0);
rt = (bool?)c.DoCall("ThreeStateTest", 1);
rt = (bool?)c.DoCall("ThreeStateTest", 2);
CustomClassAsArg ccarg = new CustomClassAsArg();
c.DoCall("UpdateCC",ccarg);
} //Main
}
}
ClassCaller.cs本身:
using System;
using System.Collections.Generic;
using System.Diagnostics;
using System.IO;
using System.Linq;
using System.Reflection;
using System.Text;
using System.Text.RegularExpressions;
using System.Threading.Tasks;
/// <summary>
/// Helper class for performing invoke function call in dynamically loaded assembly.
/// </summary>
public class ClassCaller
{
Type type;
object o;
bool throwOnError;
/// <summary>
/// Can specify class using only assembly name / class type without
/// actual class instance - if you intend to call only static methods.
/// </summary>
/// <param name="assemblyName">Assembly name</param>
/// <param name="className">Class name, including namespace</param>
/// <param name="_throwOnError">true if throw on error</param>
public ClassCaller(String assemblyName, String className, bool _throwOnError)
{
throwOnError = _throwOnError;
Assembly asm = AppDomain.CurrentDomain.GetAssemblies().Where(x => x.GetName().Name == assemblyName).FirstOrDefault();
if (asm == null)
{
if (_throwOnError)
throw new NullReferenceException("Assembly with name '" + assemblyName + "' was not found");
return;
}
type = asm.GetType(className, _throwOnError);
}
public ClassCaller(object _o)
{
type = _o.GetType();
o = _o;
}
/// <summary>
/// Gets method to invoke.
/// </summary>
/// <param name="func">Function name to get. Use '!' as a prefix if it's static function.</param>
/// <param name="types">Function argument types.</param>
/// <returns>Method to be invoked.</returns>
public MethodInfo GetFunc(String func, Type[] types)
{
bool bIsStatic = func.FirstOrDefault() == '!';
if (bIsStatic) func = func.Substring(1);
BindingFlags f = BindingFlags.Public | BindingFlags.NonPublic;
if (!bIsStatic)
f |= BindingFlags.Instance;
else
f |= BindingFlags.Static;
MethodInfo m = type.GetMethod(func, f, null, types, null);
if (m == null && throwOnError)
throw new NotSupportedException("Compatible function '" + func + "' not found");
return m;
}
//Autogenerated code starts (Do not edit)
public object DoCall(string func)
{
Type[] types = new Type[] { };
object[] args = new object[] { };
MethodInfo f = GetFunc(func, types);
if (f == null)
return null;
object r = f.Invoke(o, args);
return r;
}
public object DoCall<A1>(string func, A1 a1)
{
Type[] types = new Type[] { typeof(A1) };
object[] args = new object[] { a1 };
MethodInfo f = GetFunc(func, types);
if (f == null)
return null;
object r = f.Invoke(o, args);
return r;
}
public object DoCall<A1>(string func, ref A1 a1)
{
Type[] types = new Type[] { typeof(A1).MakeByRefType() };
object[] args = new object[] { a1 };
MethodInfo f = GetFunc(func, types);
if (f == null)
return null;
object r = f.Invoke(o, args);
a1 = (A1)args[0];
return r;
}
public object DoCall<A1, A2>(string func, A1 a1, A2 a2)
{
Type[] types = new Type[] { typeof(A1), typeof(A2) };
object[] args = new object[] { a1, a2 };
MethodInfo f = GetFunc(func, types);
if (f == null)
return null;
object r = f.Invoke(o, args);
return r;
}
public object DoCall<A1, A2>(string func, ref A1 a1, A2 a2)
{
Type[] types = new Type[] { typeof(A1).MakeByRefType(), typeof(A2) };
object[] args = new object[] { a1, a2 };
MethodInfo f = GetFunc(func, types);
if (f == null)
return null;
object r = f.Invoke(o, args);
a1 = (A1)args[0];
return r;
}
public object DoCall<A1, A2>(string func, A1 a1, ref A2 a2)
{
Type[] types = new Type[] { typeof(A1), typeof(A2).MakeByRefType() };
object[] args = new object[] { a1, a2 };
MethodInfo f = GetFunc(func, types);
if (f == null)
return null;
object r = f.Invoke(o, args);
a2 = (A2)args[1];
return r;
}
public object DoCall<A1, A2>(string func, ref A1 a1, ref A2 a2)
{
Type[] types = new Type[] { typeof(A1).MakeByRefType(), typeof(A2).MakeByRefType() };
object[] args = new object[] { a1, a2 };
MethodInfo f = GetFunc(func, types);
if (f == null)
return null;
object r = f.Invoke(o, args);
a1 = (A1)args[0];
a2 = (A2)args[1];
return r;
}
//Autogenerated code ends
public static void UpdateSourceCodeHelperFunctions( int nParametersToSupport)
{
String srcFilename = new StackTrace(true).GetFrame(0).GetFileName();
String src = File.ReadAllText(srcFilename, Encoding.UTF8);
String autogenRegex = "(Autogenerated\\scode\\sstarts.*?[\r\n]{2})(.*)([\r\n]{2}\\s+//Autogenerated\\scode\\sends)";
if (!Regex.Match(src, autogenRegex, RegexOptions.Singleline).Success)
{
Console.WriteLine("Error: Invalid source code");
return;
}
string[] argType = new String[] { "", "ref" };
String s = "";
string lf = "\r\n";
string headSpace = " ";
for (int callArgs = 0; callArgs <= nParametersToSupport; callArgs++)
{
int[] argTypes = new int[callArgs];
int iterations = (int)Math.Pow(2, callArgs);
for (int i = 0; i < iterations; i++)
{
//public object DoCall<A1, A2>(String func, A1 a1, A2 a2)
s += headSpace;
s += "public object DoCall" + ((callArgs != 0) ? "<" : "");
s += String.Join(", ", Enumerable.Range(1, callArgs).Select(n => "A" + n));
s += (callArgs != 0) ? ">" : "";
s += "(string func";
String types = "";
String paramsList = "";
bool[] isRefType = new bool[callArgs];
for (int iArg = 0; iArg < callArgs; iArg++)
{
isRefType[iArg] = (((1 << iArg) & i) != 0);
String isRef = isRefType[iArg] ? "ref " : "";
String argTypeName = "A" + (iArg + 1);
String argName = "a" + (iArg + 1);
s += ", ";
s += isRef;
s += argTypeName + " " + argName;
if (iArg != 0)
{
types += ", ";
paramsList += ", ";
}
types += "typeof(" + argTypeName + ")";
if (isRefType[iArg])
types += ".MakeByRefType()";
paramsList += argName;
} //for
s += ")";
s += lf;
s += headSpace + "{" + lf;
//Type[] types = new Type[] { typeof(A1).MakeByRefType() };
s += headSpace + " ";
if( types.Length != 0 ) types += " ";
s += "Type[] types = new Type[] { " + types + "};";
s += lf;
//object[] args = new object[] { a1 };
s += headSpace + " ";
if( paramsList.Length != 0 ) paramsList += " ";
s += "object[] args = new object[] { " + paramsList + "};";
s += lf;
//MethodInfo f = GetFunc(func, types);
//if (f == null)
// return null;
//object r = f.Invoke(o, args);
s += headSpace + " MethodInfo f = GetFunc(func, types);" + lf;
s += headSpace + " if (f == null)" + lf;
s += headSpace + " return null;" + lf;
s += headSpace + " object r = f.Invoke(o, args);" + lf;
for (int iArg = 0; iArg < callArgs; iArg++)
{
if (!isRefType[iArg])
continue;
// a1 = (A1)args[0];
String argTypeName = "A" + (iArg + 1);
String argName = "a" + (iArg + 1);
s += headSpace + " ";
s += argName + " = (" + argTypeName + ")args[" + iArg + "];";
s += lf;
}
s += headSpace + " return r;" + lf;
s += headSpace + "}" + lf;
s += lf;
}
} //for
String oldautogenCode = Regex.Match(src, autogenRegex, RegexOptions.Singleline).Groups[2].Value;
//
// Visual studio text editor configuration affects spacing. We trim here everything so we can compare output.
//
oldautogenCode = oldautogenCode.Replace(" ", "").TrimStart('\r','\n');
String newautogenCode = s.Replace(" ", "").TrimStart('\r', '\n');
String newSrc = Regex.Replace(src, autogenRegex, "$1\r\n" + s + "$3", RegexOptions.Singleline);
if (oldautogenCode == newautogenCode)
{
Console.WriteLine("Source code is up-to-date.");
}
else
{
File.WriteAllText(srcFilename, newSrc, Encoding.UTF8);
}
} //UpdateSourceCodeHelperFunctions
} //class ClassCaller
这样:
ClassCaller.UpdateSourceCodeHelperFunctions(2);
函数重新生成自动生成的代码部分以支持 - 出于演示目的,我现在只支持调用2个参数,但通常需要更多参数 - 但这会增加自动生成的代码大小。
自动生成的代码只能在调试模式下更新,而不能在发布配置中更新。 (但在发布中根本不需要这样做。)
也许这不是你问题的直接答案,但我认为这符合你的想法。
反射调用要求所有参数类型正确匹配100% - 否则反射将无法找到所需的方法。
此解决方案也有局限性 - 例如,如果某些参数是可选的,它就无法找到正确的方法 - 例如:
void DoMethod( int a, int b = 0 );
所以你可以打电话:
DoMethod(5);
但不是:
DoCall("DoMethod", 5);
它必须是完整的参数集:
DoCall("DoMethod", 5, 0);
我知道通过反射调用的方法在消耗时间方面可能很昂贵,所以在使用之前要三思而后行。
更新31.5.2016 我还发现C#&#39;动态&#39;关键字也可以用于特定方法的动态调用,而不需要知道反射方法调用细节,但动态只对实例进行操作,使用ClassCaller更容易进行静态方法调用。