将重写附加到 C# 类

发布于 2024-11-05 01:41:52 字数 2362 浏览 3 评论 0原文

我有一个带有函数的类:

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 ,然后将 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函数,有时基于随机值不会调用基本函数。

编辑:真正解决这个混乱的方法是以某种方式使用反射来更改虚拟方法表,我将在一个单独的问题上进行后续处理。我将这个问题保留在这里,以防有人找到更好的方法来做到这一点。

I have a class with functions:

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
    }
}

I need in my code when I attach AttachmentA to MyClass that all the functions in MyClass that are also present in AttachmentA to be overridden by the functions in AttachmentA and also give access to the original functions in MyClass.
For example, I create MyClass and then attach AttachmentA instance to it. calling MyClass.A() will actually call AttachmentA.A() and the AttachmentA.RealA() will call the base function that was overridden.
I know this can be somehow done with something like using event handlers lists to handle overrides but is there an easy way to implement this?
Edit: I have no problem with long code that uses reflection as long as its present once and doesn't have to be even mentioned in any of the functions - maybe only when attaching attachement.

Edit: you wanted an example:

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
    }
}

Edit: What I want to achieve here is like unit with attributes( = attachments). When the unit get an attachment of RandomSpeed, RandomSpeed will override the unit's GetSpeed and return random value. when it will get an attachment of evasion, it will override that units ReduceHP function and sometimes based on random value will not call the base function.

Edit: What will really solve this mess is to somehow use reflection to change virtual method tables, I'm gonna make a followup on a separate question. I keep this question here incase someone find a better way to do this.

如果你对这篇内容有疑问,欢迎到本站社区发帖提问 参与讨论,获取更多帮助,或者扫码二维码加入 Web 技术交流群。

扫码二维码加入Web技术交流群

发布评论

需要 登录 才能够评论, 你可以免费 注册 一个本站的账号。

评论(5

吹梦到西洲 2024-11-12 01:41:52

正如评论中提到的,装饰器模式就是您正在寻找的。

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

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

As mentioned in comments, Decorator Pattern is what you are looking for.

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

The decorator pattern can be used to
make it possible to extend (decorate)
the functionality of a certain object
at runtime, independently of other
instances of the same class, provided
some groundwork is done at design
time. This is achieved by designing a
new decorator class that wraps the
original class.

愛放△進行李 2024-11-12 01:41:52

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

如果接口尚未生效,则让它们返回 null,然后您可以检查它们是否都返回 null 并根据需要调用基类,或者传入一个 ref 参数,您可以根据需要调整该参数。

Why not take another approach? Have attachments implement interfaces based on what they want to override, for example ISpeedAttachment. Then you could, in the base speed function loop through attachments which implement ISpeedAttachment, calling them.

Have the interfaces return null if they haven't taken effect and you could then check they've all returned null and call the base class as appropriate, or pass in a ref parameter which you could adjust as necessary.

ㄖ落Θ余辉 2024-11-12 01:41:52

您应该研究一下行为模式。对于您的特定问题,我建议使用 责任链策略模式

You should look into the behavioral patterns. For your particular problem I would recommend either the chain of responsibility or the strategy pattern.

你是年少的欢喜 2024-11-12 01:41:52

如果您不想引入依赖项,例如在 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 在实际场景中具有相同的参数和返回类型,则可以将其缩短为一种委托类型。

If you don't want to introduce dependencies, like inheritance or interfaces to implement on MyClass then:

You can achieve this through delegates.

Long story short, you cannot override function in runtime without resorting to some obscure reflection magic, but you can declare delegates instead of functions. When you construct your class in the constructor fill the delegates with private methods which will be used for as long as no AttachmentA class comes in. And use those delegates instead of the methods.

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");
        }
    }
}

If you need the execution to start always in MyClass instead of the Attachment class you can wrap the delegates like here:

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");
        }
    }
}

You can shorten this to one delegate type if A and B have the same parameters and return type in your real scenario.

诺曦 2024-11-12 01:41:52

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

标准的做法是这样的:

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
    }
}

这只允许添加一个附件。如果您想分别覆盖多个函数的行为,您可以使用某种集合来保存附件。在基类中,您需要循环遍历它们并找到您想要执行的类。

I'm not sure if dynamically overriding a class's functionality is possible, but you can achieve something similar by using different interfaces. Depending on the context you want to use this in, it may require only small redesign.

The standard way of doing it would be this:

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
    }
}

Here's another example, similar to what blowdart suggested:

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
    }
}

This only allows for a single attachment to be added. If you wanted to override the behavior of several functions separately, you could use a Collection of some sort to hold the attachments. Within the base class you'd need to loop through them and find the one you want to execute.

~没有更多了~
我们使用 Cookies 和其他技术来定制您的体验包括您的登录状态等。通过阅读我们的 隐私政策 了解更多相关信息。 单击 接受 或继续使用网站,即表示您同意使用 Cookies 和您的相关数据。
原文