更改特定对象的方法调用

时间:2016-10-11 09:38:10

标签: c# reflection

this问题让我想知道使用C#的暗反射方式是否可能有类似的东西。

说我有这段代码:

public class Foo
    {
        public void FooPrint() // can't change this implementation
        {
            Console.Write("Foo");
        }
    }

public class Bar
{
    public Foo foo = new Foo();
    public Bar()
    {
        //do some reflection magic with member foo here ?
    }

    public void FooPrintRewritten()
    {
        Console.Write("Haha, only for Bar.foo."); 
    }
}

class Program
{
    static void Main(string[] args)
    {
        Foo a = new Foo();
        a.FooPrint(); // should still print "Foo"

        Bar bar = new Bar();
        bar.foo.FooPrint(); // should print "Haha, only for Bar.foo."
    }
}

我在内联评论中以任何可能的方式提出的要求是什么?有没有办法将方法调用重新绑定到另一个方法只用于特定变量?

是的我知道这很荒谬,不应该在生产代码中使用它。这是为了好奇。

4 个答案:

答案 0 :(得分:1)

这个怎么样,仍有相同的用法:

{{1}}

答案 1 :(得分:1)

实际上,如果您需要单独实现以维护原则(取决于您遵守SOLID ofc的严格程度),则在SOLID的SRP下存在此类行为的情况。

它被称为拦截器模式,由Mocking库使用,例如Moq。

有关如何使用该模式的一个很好的示例,请查看以下关于该主题的文章:C#: Why Decorate When You Can Intercept

答案 2 :(得分:1)

这是在实例化类

时覆盖(虚拟)方法的另一种方法
class Program
{
    static void Main(string[] args)
    {
        Foo a = new Foo();
        a.FooPrint(); // should still print "Foo"

        Bar bar = new Bar();
        bar.foo.FooPrint(); // should print "Haha, only for Bar.foo."
        Console.Read();
    }
}
public class Foo
{
    public Action FooPrint = () => Console.WriteLine("Foo");
}

public class Bar
{
    public Foo foo = new Foo()
    {
        FooPrint = () => Console.WriteLine("Haha, only for Bar.foo.")
    };
}

this帖子解释了在实例化类时如何正确覆盖。

但是,它使用 Func ,这需要一个包含无效返回类型的方法,这就是为什么你要使用 Action ,而不是{{ 3}}

答案 3 :(得分:1)

其他答案已经提出了一些方法,你可以在功能层面上实现你想要的东西,这可以说是理智的事情,但我会直接解决这个问题:这可以改变吗?问题除了Bar.Bar()的实施,保留Bar.foo类型Foo并且不更改Foo时的任何内容?

答案是否定的。您无法更改单个对象的方法表,这基本上是您在此处要求的。方法表是类型的一部分,而不是实例。如果表达式f的类型为Foo,而FooPrintFoo的非虚方法,则调用f.FooPrint()将始终解析为{{ 1}}。更糟糕的是,编译器可能会选择内联调用,因为这显然是一个安全的优化 * 。现在你的黑暗反射方式在哪里?

实现这一目标的唯一方法是说服编译器应该特别对待Foo.FooPrint的调用,并考虑实例。有几种方法可以这样做:

  • Foo.FooPrint可以成为代表。委托调用的目标是每个委托实例特定的。
  • Foo.FooPrint可以成为虚拟,抽象或接口方法。所有这些都是基于实例的运行时类型来解决的。只需从Foo.FooPrint派生一个课程即可离开。
  • Foo可以继承自Foo。 MBRO(通常称为MBRO)由抖动专门处理,因为(顾名思义)可能需要对调用进行编组。特别是,如果MarshalByRefObject是一个MBRO,你可以为它创建一个RealProxy,它会在几乎所有方面咳出一个类似真实的Foo的透明代理,直到Foo,但您必须选择实际处理呼叫的方式。

所有这些方法都被各种模拟/拦截器/代理库使用,所有这些都需要某些更改为GetType()。唯一不需要(文本)更改Foo的方法是那些重写IL所涉及的方法,比如PostSharp或Microsoft Fakes,但我认为这个问题的目的是作弊。

* 从技术上讲,C#标准没有提及任何方法表或允许的内联方式,因为这些是实现细节,但确实说Foo总是只用一种方式解决不考虑实例(除了它不能是Foo.FooPrint)。