【问题标题】:C# Changing class method at run-time [duplicate]C#在运行时更改类方法[重复]
【发布时间】:2010-05-03 16:40:31
【问题描述】:

我需要扩展实例的行为,但我无权访问该实例的原始源代码。例如:

/* I don't have the source code for this class, only the runtime instance */
Class AB
{
  public void execute();
}

在我的代码中,我会拦截每个执行调用,计算一些 sutff,然后调用原始执行,类似于

/* This is how I would like to modify the method invokation */
SomeType m_OrgExecute;

{
    AB a = new AB();
    m_OrgExecute = GetByReflection( a.execute );
    a.execute = MyExecute;
}

void MyExecute()
{
    System.Console.Writeln( "In MyExecute" );
    m_OrgExecute();
}

这可能吗?

有人有解决这个问题的办法吗?

【问题讨论】:

    标签: c# .net runtime


    【解决方案1】:

    您似乎想要Decorator pattern

    class AB
    {
       public void execute() {...}
    }
    
    class FlaviosABDecorator : AB
    {
       AB decoratoredAB;
    
       public FlaviosABDecorator (AB decorated)
       {
           this.decoratedAB = decorated;
       }
    
       public void execute()
       {
           FlaviosExecute();  //execute your code first...
           decoratedAB.execute();
       }
    
       void FlaviosExecute() {...}
    }
    

    然后您必须修改使用 AB 对象的代码。

    //original code
    //AB someAB = new AB();
    
    //new code
    AB originalAB = new AB();
    AB someAB = new FlaviosABDecorotor(originalAB);
    
    /* now the following code "just works" but adds your method call */
    

    【讨论】:

    • 这有一个严重的缺点:所有被编程为使用AB 的第三方代码将继续使用原始类。
    【解决方案2】:

    没有办法直接通过反射等来做到这一点。

    为了像这样注入您自己的代码,您需要创建其程序集的修改版本,并使用某种形式的code injection。您不能只在运行时“更改任意程序集的方法”。

    【讨论】:

      【解决方案3】:

      我会看看 PostSharp。它可以“重新连接”现有的已编译程序集,以添加您正在寻找的处理前后的类型。我不能 100% 确定它会满足您的需求,但很有可能。

      http://www.sharpcrafters.com/aop.net

      【讨论】:

      • 这需要对程序集进行属性化,因为这是 post sharp 确定要重写的内容的方式。
      • @Reed:您可以在 PostSharp 中使用 [assembly:] 级属性。也就是说,那些可能必须进入程序集的 AssemblyInfo.cs。
      【解决方案4】:

      您可以使用动态代理,例如 Castle Proxy

      【讨论】:

        【解决方案5】:

        因为我更喜欢组合而不是继承(而且如果类是密封的,继承可能不是一个选项),我会将AB 包装在我自己的名为FlaviosAB 的类中......

        public class FlaviosAB
        {
            private AB _passThrough;
            public FlaviosAB(){
                _passThrough = new AB();
            }
        
            public void execute()
            {
                //Your code...
                Console.WriteLine("In My Execute");
                //Then call the passThrough's execute.
                _passThrough.execute();
            }
        }
        

        【讨论】:

        • 这解决了原始类被密封的任何问题。根据调用者需要多少 AB 上的其他公共方法,可能需要通过包装器表达更多。
        • @jdk:可能需要更多,但问题中没有指定,所以我没有包括它:) 另外,你的 gravatar 看起来就像我的纹身 :)
        • 如果你的纹身图片是在互联网上发布的,那我就偷了它作为我的 Gravatar。我只能给你的答案+1一次,否则我会为酷纹身提供第二次! :)
        【解决方案6】:

        您总是可以从类继承并覆盖 execute() 方法(如果该类不是密封的,并且该方法至少不是私有的。)

        【讨论】:

          【解决方案7】:

          如果 AB 不是密封的,那么你可以继承类并覆盖方法。在这种情况下使用新的

          class ABChild : AB {
              public new void execute() {
                  System.Console.Writeln( "In MyExecute" );
              }
          }
          

          根据 cmets,您应该在 ABChild 类中调用此 new 方法:

          void Invoke() {
              ABChild a = new ABChild();
              a.execute();
          }
          

          希望对你有帮助!!

          【讨论】:

          • 如果 execute() 的调用者正在使用 AB,这将不起作用。除非您将其强制转换为 ABChild,否则它将调用 AB 上的原始方法。
          【解决方案8】:

          您可以实现动态代理。查找更多信息here

          基本上,您正在扩展基类并用您的方法覆盖一些方法。现在您需要重新分配由您的实例调用的对象,所有调用都将首先通过您的对象。

          【讨论】:

            【解决方案9】:

            也许Stackoverflow thread 中的面向方面编程 (AOP) 解决方案之一会派上用场...

            【讨论】:

              【解决方案10】:

              您可以使用包装类:

              Class ABWrapper
              {
                private AB m_AB;
              
                ABWrapper( AB ab )
                {
                  m_AB = new AB();
                }
              
                public void execute()
                {
                  // Do your stuff, then call original method
                  m_AB.execute();
                }
              }
              

              AB 实现一个接口时,这是一个很好的方法(不过你没有提到)。在这种情况下,ABWrapper 应该实现相同的接口。当您使用 factory 甚至 依赖注入 创建 AB 实例时,您可以使用包装器轻松替换它们。

              【讨论】:

                猜你喜欢
                • 1970-01-01
                • 2020-03-05
                • 2013-11-12
                • 1970-01-01
                • 2014-05-03
                • 2011-07-11
                • 2018-01-15
                • 1970-01-01
                • 2016-02-02
                相关资源
                最近更新 更多