我想做的是用另一个方法的主体替换(静态)方法的主体,同时保留原始方法的主体。
让我从一个例子开始:
public static class Program
{
public static void Main(string[] args)
{
switch (new Random().Next(3))
{
case 0:
// Change MainFunction's body into a's body, storing the original body somewhere else
break;
case 1:
// Change MainFunction's body into b's body, storing the original body somewhere else
break;
case 2:
// Change MainFunction's body into c's body, storing the original body somewhere else
break;
}
MainFunction(null);
}
public static void MainFunction(object someParameter)
{
Console.Write("The method called is: ");
}
private static void a(object someParameter)
{
// Call the 'base' method
Console.WriteLine("a");
}
private static void b(object someParameter)
{
// Call the 'base' method
Console.WriteLine("c");
}
private static void c(object someParameter)
{
// Call the 'base' method
Console.WriteLine("c");
}
}
期望的输出是:'调用的方法是:'和'a', 'b'或'c'。我知道这个例子是完全无用的,这就是为什么它是一个例子,但我只是想知道如何实现这一点。
有人知道如何实现这个吗?
编辑:
我是而不是在寻找委托,我知道它们非常有用,但正如我所说的,这是一个例子,我知道如何实现它。
我也知道你应该重用随机,以避免重新计算初始数据的开销,并给GC一些休息。
这个例子只是为了证明结果,当然这个例子可以很容易地解决使用delegate
, Action<T>
,或者如果你想要返回值也Func<TOut>
,但我不是在寻找解决方案。
听起来你真正想要的是一个委托。
您可以创建Action<object>
类型的委托,并在代码中分配它,然后在适当的MainFunction
中调用该委托。
private Action<object> MainFunction;
private Random rand = new Random();
public static void Main(string[] args)
{
switch (rand.Next(3))
{
case 0: MainFunction = a; break;
case 1: MainFunction = b; break;
case 2: MainFunction = c; break;
}
MainFunction(null);
}
同样,虽然可能只是为了说明目的,但在代码中使用new Random()
可能不是您想要的。创建Random
的新实例将重置随机数序列。除非您只获取这一个随机值,否则您可能希望存储随机数生成器。
EDIT: 我认为你需要提供一个更好的解释,为什么使用委托的方法不适合你的问题。确定你需要的关键需求和一个完整的、相关的例子将有助于你得到一个令你满意的答案。一般来说,在运行时替换现有类的方法体是不可能的——这将违反CLR安全模型。你能做的最好的事情就是在运行时生成一个新的类,并发出带有适当签名和主体的方法——然而,我不清楚这与使用委托有什么不同。
或者,你可以使用PostSHARP之类的东西,并使用面向方面的技术来改变运行时MainFunction()的行为。例如,PostSHARP中的MethodBoundaryAspect允许您在方法之前、之后或代替方法运行任意代码。然而,如果没有一个更好的解释你到底想要解决什么问题,我们能做的最好的就是猜测…这不会让你走得太远。
c#是一种静态类型语言。这意味着编译器读取您的c#代码,并将其转换为汇编语言的。exe或。dll文件。
据我所知,当编译器从你的代码中生成一个方法时,方法体完全独立于方法的名称。方法的名称只是一个与一段代码相关联的属性。
运行时引擎使用自顶向下的逻辑;它从程序的开头(Main
方法)开始,每次执行一条语句。例如,当它遇到与"Call MyMethod"相当的汇编语言时,它只是移动到与"MyMethod"相关的代码块中的第一个语句,并从那里开始执行。在"MyMethod"代码块中的所有语句都被执行之后,运行时引擎返回到MyMethod被调用之前的位置。
- 允许您和其他使用您的代码或程序集的程序员识别方法并将其与其他方法区分开来
- 允许编译器知道与源代码中的方法调用关联的程序集级方法
- 最后,在运行时执行方法的相关代码块。
你试图做的事情绕过了为每个方法拥有唯一签名的所有目的。现在,我知道这只是一个示例程序,所以我不会指出这是什么不好的做法。然而,我担心在运行时从关联的主体中删除方法的名称是完全不可能的,除非动态地定义一个新方法,或者像前面提到的那样,使用委托。
回到c#的静态特性,你要做的是动态类型语言(如JavaScript、PHP、Lua等)更常见的特性。例如,在JavaScript中,您可以定义一个函数(相当于JavaScript中的方法),将其赋值给一个变量,然后随意修改变量的值,然后动态地调用它。使此功能成为可能的是代码的执行方式:与c#和其他静态类型语言相比,每个语句都作为人类可读的源代码读取,解释为一个或多个可执行的计算机可读代码语句,并执行,然后运行时引擎继续执行下一个语句(或开始执行函数调用,视情况而定)。
据我所知,真正交换方法体是不可能的。但根据你的设置,你可以使用委托来解决它。这里有一些伪代码给你一个方向:
public static Action<object> MethodToCall;
然后在0的情况下,例如:
MethodToCall = (someParameter) => {
MainFunction(someParameter);
a(someParameter);
}
然后调用:
而不是MainFunction(null)MethodToCall(null)
如果静态方法是在动态模块或程序集中定义的,您可以使用System.Reflection.Emit.MethodRental.SwapMethodBody
来做您想做的事情,但它只适用于来自动态模块的方法。
您在寻找Action
吗?(大标题)
public static Random rand = new Random();
public static void Main(string[] args)
{
Action<string[]> action = null;
switch (rand.Next(3))
{
case 0:
action = a;
break;
case 1:
action = b;
break;
case 2:
action = c;
break;
}
action(null);
}
(也固定随机为您,应该重用实例,而不是创建一个新的非常频繁,序列将不会是随机的,否则)