我有一个功能类:
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
时,我需要在MyClass
中AttachmentA
中AttachmentA
中的所有函数都被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}}函数,有时候基于随机值不会调用基函数。
编辑:真正解决这个问题的方法是以某种方式使用反射来改变虚拟方法表,我将在一个单独的问题上进行跟进。我在这里提出这个问题,因为有人找到了更好的方法来做到这一点。
答案 0 :(得分:4)
正如评论中所提到的,Decorator Pattern正是您所寻找的。 p>
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 responsibility或strategy 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
}
}
这仅允许添加单个附件。如果要单独覆盖多个函数的行为,可以使用某种类型的集合来保存附件。在基类中,你需要遍历它们并找到你想要执行的那个。