将覆盖附加到C#类

时间:2011-05-01 13:42:46

标签: c# override aop

我有一个功能类:

class MyClass
{
    public List<Attachment> Attachments;

    public void A()
    {
        // Do something
    }
    public void B()
    {
        // Do something
    }
}

class AttachmentA : Attachment
{
    public void A()
    {
        // Do something else
        RealA();
    }
}

class AttachmentB : Attachment
{
    public void B()
    {
        // Do something else
        // RealB();
        // No need to call base function here
    }
}

当我将AttachmentA附加到MyClass时,我需要在MyClassAttachmentAAttachmentA中的所有函数都被MyClass中的所有函数覆盖,以便被MyClass中的函数覆盖{1}}并且还可以访问AttachmentA中的原始函数 例如,我创建MyClass.A(),然后将AttachmentA.A()实例附加到其中。调用AttachmentA.RealA()实际上会调用class MyClass { public List<Attachment> Attachments; public MyClass() { Attachments = new List<Attachment>(); } public void Attach(Attachment attachment) { Attachments.Add(attachment); // Do some magic here } public void A() { Console.WriteLine("MyClass.A"); } public void B() { Console.WriteLine("MyClass.B"); } } class AttachmentA : Attachment { public void A() { Console.WriteLine("AttachmentA.A"); RealA(); } } class AttachmentB : Attachment { public void B() { Console.WriteLine("AttachmentB.B"); } } public class Program { public static void Main(string[] Args) { MyClass aaa = new MyClass(); aaa.A(); // should print MyClass.A aaa.B(); // should print MyClass.B aaa.Attach(new AttachmentA()); aaa.Attach(new AttachmentB()); aaa.A(); // should print AttachmentA.A <newline> MyClass.A aaa.B(); // should print AttachmentB.B } } RandomSpeed将调用被覆盖的基本函数。
我知道这可以通过某种方式完成,例如使用事件处理程序列表来处理覆盖,但是有一种简单的方法来实现它吗? 编辑:我对使用反射的长代码没有任何问题,只要其存在一次,并且不必在任何函数中提及 - 可能仅在附加附件时。

编辑:你想要一个例子:

RandomSpeed

编辑:我想在这里实现的就像带有属性的单元(=附件)。当该单元获得GetSpeed的附件时,ReduceHP将覆盖该单位的{{1}}并返回随机值。当它获得逃避附件时,它将覆盖单位{{1}}函数,有时候基于随机值不会调用基函数。

编辑:真正解决这个问题的方法是以某种方式使用反射来改变虚拟方法表,我将在一个单独的问题上进行跟进。我在这里提出这个问题,因为有人找到了更好的方法来做到这一点。

5 个答案:

答案 0 :(得分:4)

正如评论中所提到的,Decorator Pattern正是您所寻找的。

http://en.wikipedia.org/wiki/Decorator_pattern

  

可以使用装饰器模式   使扩展(装饰)成为可能   某个对象的功能   在运行时,独立于其他   提供了同一类的实例   一些基础工作是在设计上完成的   时间。这是通过设计a来实现的   包装的新装饰器类   原班。

答案 1 :(得分:2)

如果你不想引入依赖关系,比如在MyClass上实现继承或接口,那么:

您可以通过代表来实现这一目标。

长话短说,你不能在运行时覆盖函数而不使用一些模糊的反射魔法,但你可以声明代理而不是函数。在构造函数中构造类时,只要没有AttachmentA类,就会使用私有方法填充委托。并使用这些委托而不是方法。

using System;
using System.Collections.Generic;
using System.Linq;
using System.Text;
using System.Globalization;
using System.Runtime.InteropServices;

namespace ConsoleApplication1
{
    static class Program
    {

        static void Main(string[] args)
        {
            MyClass aaa = new MyClass();
            aaa.A(); // should print MyClass.A
            aaa.B(); // should print MyClass.B
            aaa.Attach(new AttachmentA());
            aaa.Attach(new AttachmentB());
            aaa.A(); // should print AttachmentA.A <newline> MyClass.A
            aaa.B(); // should print AttachmentB.B
        }

    }

    class MyClass
    {
        public List<Attachment> Attachments;

        public MyClass()
        {
            A = _A;
            B = _B;
            Attachments = new List<Attachment>();
        }
        public void Attach(Attachment attachment)
        {
            Attachments.Add(attachment);

            // this is your magic
            if (attachment.GetType() == typeof(AttachmentA)) {
                A = ((AttachmentA)attachment).A;
            }
            else if (attachment.GetType() == typeof(AttachmentB))
            {
                B = ((AttachmentB)attachment).B;
            }
        }

        public delegate void delegateA();
        public delegate void delegateB();

        public delegateA A;
        public delegateB B;

        public void _A()
        {
            Console.WriteLine("MyClass.A");
        }
        public void _B()
        {
            Console.WriteLine("MyClass.B");
        }
    }

    class Attachment { 
    }

    class AttachmentA : Attachment
    {
        public void A()
        {
            Console.WriteLine("AttachmentA.A");
        }
    }

    class AttachmentB : Attachment
    {
        public void B()
        {
            Console.WriteLine("AttachmentB.B");
        }
    }
}

如果您需要始终在MyClass而不是Attachment类中启动执行,您可以像这样包装代理:

using System;
using System.Collections.Generic;
using System.Linq;
using System.Text;
using System.Globalization;
using System.Runtime.InteropServices;

namespace ConsoleApplication1
{
    static class Program
    {

        static void Main(string[] args)
        {
            MyClass aaa = new MyClass();
            aaa.A(); // should print MyClass.A
            aaa.B(); // should print MyClass.B
            aaa.Attach(new AttachmentA());
            aaa.Attach(new AttachmentB());
            aaa.A(); // should print AttachmentA.A <newline> MyClass.A
            aaa.B(); // should print AttachmentB.B
        }

    }

    class MyClass
    {
        public List<Attachment> Attachments;

        public MyClass()
        {
            Attachments = new List<Attachment>();
        }

        public void Attach(Attachment attachment)
        {
            Attachments.Add(attachment);

            if (attachment.GetType() == typeof(AttachmentA)) {
                _A = ((AttachmentA)attachment).A;
            }
            else if (attachment.GetType() == typeof(AttachmentB))
            {
                _B = ((AttachmentB)attachment).B;
            }
        }

        public delegate void delegateA();
        public delegate void delegateB();

        public delegateA _A;
        public delegateB _B;

        public void A()
        {
            if (_A != null)
            {
                _A();
            }
            else
            {
                Console.WriteLine("MyClass.A");
            }
        }
        public void B()
        {
            if (_B != null)
            {
                _B();
            }
            else
            {
                Console.WriteLine("MyClass.B");
            }
        }
    }

    class Attachment { 
    }

    class AttachmentA : Attachment
    {
        public void A()
        {
            Console.WriteLine("AttachmentA.A");
        }
    }

    class AttachmentB : Attachment
    {
        public void B()
        {
            Console.WriteLine("AttachmentB.B");
        }
    }
}

如果A和B在真实场景中具有相同的参数和返回类型,则可以将其缩短为一个委托类型。

答案 2 :(得分:2)

为什么不采取另一种方法?让附件根据要覆盖的内容实现接口,例如ISpeedAttachment。然后你可以在基本速度函数循环中通过实现ISpeedAttachment的附件来调用它们。

如果接口没有生效,接口会返回null,然后你可以检查它们是否都返回null并根据需要调用基类,或传入一个你可以根据需要调整的ref参数。

答案 3 :(得分:2)

您应该查看behavioral patterns。对于您的特定问题,我建议使用chain of responsibilitystrategy pattern

答案 4 :(得分:1)

我不确定是否可以动态覆盖类的功能,但是您可以通过使用不同的接口来实现类似的功能。根据您要使用它的上下文,可能只需要很小的重新设计。

这样做的标准方法是:

using System;

class MyClass
{
    public virtual void A()
    {
        Console.WriteLine("MyClass.A");
    }
    public virtual void B()
    {
        Console.WriteLine("MyClass.B");
    }
}

class ClassA : MyClass
{
    public override void A()
    {
        Console.WriteLine("AttachmentA.A");

        base.A();
    }
}

class ClassB : MyClass
{
    public override void B()
    {
        Console.WriteLine("AttachmentB.B");
    }
}

public class Program
{
    public static void Main(string[] Args)
    {
        MyClass aaa = new ClassA();
        MyClass bbb = new ClassB();

        aaa.A(); // prints MyClass.A
        aaa.B(); // prints MyClass.B
        (aaa as ClassA).A(); // prints AttachmentA.A
        (aaa as ClassA).B(); // prints MyClass.B
        bbb.A(); // prints MyClass.A
        bbb.B(); // prints MyClass.B
        (bbb as ClassB).A(); // prints AttachmentB.A + MyClass.A
        (bbb as ClassB).B(); // prints AttachmentB.B
    }
}

这是另一个例子,类似于blowdart所建议的:

interface ICallMe
{
    bool A();
    bool B();
}

class MyClass
{
    public ICallMe Attachment { get; set; }

    public void A()
    {
        bool BaseFunction = true;
        if (Attachment != null)
            BaseFunction = Attachment.A();

        if (BaseFunction)
            Console.WriteLine("MyClass.A");
    }

    public void B()
    {
        bool BaseFunction = true;
        if (Attachment != null)
            BaseFunction = Attachment.B();

        if (BaseFunction)
            Console.WriteLine("MyClass.B");
    }
}

class ClassA : ICallMe
{
    public bool A()
    {
        Console.WriteLine("AttachmentA.A");

        return true;
    }

    public bool B()
    {
        Console.WriteLine("AttachmentA.B");

        return false;
    }
}

static class Program
{

    static void Main(string[] args)
    {
        MyClass aaa = new MyClass();
        aaa.A(); // prints MyClass.A
        aaa.B(); // prints MyClass.B
        aaa.Attachment = new ClassA();
        aaa.A(); // should print AttachmentA.A <newline> MyClass.A
        aaa.B(); // should print AttachmentB.B
    }
}

这仅允许添加单个附件。如果要单独覆盖多个函数的行为,可以使用某种类型的集合来保存附件。在基类中,你需要遍历它们并找到你想要执行的那个。