用另一个方法体替换方法体

时间:2011-04-23 20:10:27

标签: c# reflection

我想要做的是用另一个方法的主体替换(静态)方法的主体,同时保留原始方法的主体。

让我先举一个例子:

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'。 我知道这个例子完全没用,这就是为什么它是一个例子,但我只是想知道如何实现这个。

有人知道如何实现这个目标吗?

修改
寻找代表,我知道他们非常有用,但正如我所说,这是一个例子,我知道如何实现这一目标。

我也知道你应该重新使用randoms以避免重新计算初始数据的开销,并让GC休息一下。

示例只是为了演示结果,当然这个示例可以使用delegateAction<T>轻松解决,或者如果您想要返回值Func<TOut>,我也可以我没有找那个解决方案。

5 个答案:

答案 0 :(得分:4)

听起来你真正想要的是代表。

您可以创建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的新实例将重置随机数序列。除非您只获取这个随机值,否则您可能希望存储随机数生成器。

编辑:我认为您需要更好地解释为什么使用代理的方法不足以解决您的问题。确定您需要的关键要求以及完整的相关示例将会持续很长时间得到满足你的答案的方式。通常,不可能在运行时替换现有类的方法体 - 这将违反CLR安全模型。您可以做的最好的事情是在运行时生成一个新类,并使用适当的签名和实体发出方法 - 但是,我不清楚这与使用委托有何不同。

或者,您可以使用PostSHARP之类的东西,并使用面向方面的技术在运行时更改MainFunction()的行为。例如,PostSHARP中的MethodBoundaryAspect允许您在方法之前,之后或代替方法运行任意代码。但是,如果没有更好地解释你正在尝试解决的问题,我们能做的最好的事情就是猜猜......这对你来说不会太远。

答案 1 :(得分:3)

C#是一种static-typed语言。这意味着编译器会读取您的C#代码,并将其转换为汇编语言.exe或.dll文件。

据我了解,当编译器从您的代码生成方法时,方法体完全独立于方法的名称。方法的名称只是与一大块代码相关联的属性。

运行时引擎使用自上而下的逻辑;它从程序的开头(Main方法)开始,并一次执行一个语句。例如,当它遇到与“调用MyMethod”相当的汇编语言时,它只是移动到与“MyMethod”相关联的代码块中的第一个语句,并从那里开始执行。在执行“MyMethod”代码块中的所有语句之后,运行时引擎将返回到调用MyMethod之前的位置。

因此,方法名称的3个主要用途是

  1. 允许您和其他程序员使用您的代码或程序集识别该方法并将其与其他方法区分开来
  2. 允许编译器知道与源代码中的方法调用关联的汇编级方法
  3. 最后,在运行时执行该方法的相关代码块。
  4. 您尝试做的事情绕过了为每种方法设置唯一签名的所有目的。现在,我知道这只是一个示例程序,所以我不会指出这是什么坏习惯。但是,我担心在运行时从其关联的主体中移除方法的名称是完全不可能的,没有defining a new method dynamically,或者如前所述,使用委托。

    回到C#的静态特性,你要做的是dynamic-typed语言的一个更常见的特性,如JavaScript,PHP,Lua等。例如,在JavaScript中,您可以定义一个函数(等效于方法的JavaScript),将其分配给变量,稍后根据需要修改变量的值,并在运行时调用它。使该功能成为可能的是代码的执行方式:与C#和其他静态类型语言相比,每个语句都被读作人类可读的源代码,解释为一个或多个可执行计算机可读代码的语句,并执行,并且运行时引擎移动到下一个语句(或者到函数调用的开头,视情况而定)。

答案 2 :(得分:2)

据我所知,不可能真正交换方法的主体。但根据您的设置,您可以使用代表解决它。这里有一些伪代码给你一个方向:

public static Action<object> MethodToCall;

然后以0为例:

MethodToCall = (someParameter) => {
    MainFunction(someParameter);
    a(someParameter);
}

然后调用:

而不是MainFunction(null)
MethodToCall(null)

答案 3 :(得分:2)

如果在动态模块或程序集中定义静态方法,则可以使用System.Reflection.Emit.MethodRental.SwapMethodBody完成您想要执行的操作,但它仅适用于动态模块中的方法。

答案 4 :(得分:1)

您在寻找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);
}

(同样为你修复Random,应该重复使用实例,而不是经常创建一个新实例,否则序列不会是随机的)