如何在不知道调用者 class 的情况下订阅对象?

How to subscribe objects without knowing their invoker's class?

我有一个 Scenario,它使用 A​​ 或几个 A​​sA​​ 是在 Scenario 之外创建的,但是 B 是在 [=16] 内部创建的=]A,不知道它的实例场景。这就是想法,A 和 B 不必知道 Scenario 是如何工作的,他们只需要在 UpdateEvent 为 raised/invoked.

时工作
using System;
using System.Collections.Generic;
using System.Linq;
using System.Text;
using System.Threading.Tasks;

namespace ConsoleApp
{
    class Program
    {
        static void Main(string[] args)
        {

            var sce1 = new Scenario();
            sce1.Add(new A(1));
            sce1.Add(new A(3));
            sce1.Update(2);
            sce1.Update(5);

            var sce2 = new Scenario();
            sce2.Add(new A(6));
            sce2.Update(0.3);

            Console.ReadKey();

        }
    }

    public class BasicEvent : EventArgs
    {
        public double Number { get; set; }
    }

    public abstract class toBeUpdated
    {
        public abstract void OnUpdate(object sender, BasicEvent e);
    }

    public class C : toBeUpdated
    {
        public double Number { get; set; }
        public C(double num)
        {
            Number = num * num;
        }
        public override void OnUpdate(object sender, BasicEvent e)
        {
            Number = Math.Pow(e.Number, 2);
        }
    }

    public class B : toBeUpdated
    {
        public double  Number { get; set; }
        public B(double num)
        {
            Number = num;
        }
        public override void OnUpdate(object sender, BasicEvent e)
        {
            Number *= e.Number;
        }
    }

    public class A : toBeUpdated
    {
        private B B1 { get; set; }
        private B B2 { get; set; }
        private C C1 { get; set; }
        public A(double number)
        {
            B1 = new B(number);
            B2 = new B(number);
            C1 = new C(number);
        }
        public override void OnUpdate(object sender, BasicEvent e)
        {
            Console.WriteLine($"Updating A.. Data = {B1.Number + B2.Number * C1.Number}");
        }
    }

    public class Scenario
    {
        private event EventHandler<BasicEvent> UpdateEvent;
        private List<toBeUpdated> toKeepTrack { get; set; }
        public Scenario()
        {
            toKeepTrack = new List<toBeUpdated>();
        }
        public void Add(toBeUpdated obj)
        {
            toKeepTrack.Add(obj);
            UpdateEvent += obj.OnUpdate;
        }
        public void doSomethingWithTheList()
        {
            ....
        }
        public void Update(double num)
        {
            UpdateEvent?.Invoke(this, new BasicEvent() { Number = num });
            Console.WriteLine();
        }
    }
}

我需要创建多个场景,我无法将其设为静态。

对于这个用例,我认为最好的方法是创建一个接口 IScenario 并让 Scenario 扩展这个接口。这将 隐藏 Scenario 的实现细节,并允许您将 IScenario 直接传递到 A.

的构造函数中
public interface IScenario
{
    void Add(toBeUpdated obj);
    void Update(int num);
}

您现在可以像这样扩展 Scenario

public class Scenario : IScenario
{
    ...
}

相应地更新A

public class A : toBeUpdated
{
    ...
    public A(int number, IScenario scenario)
    {
        B1 = new B(number);
        scenario.Add(this);
        scenario.Add(B1);
    }
    ...
}

使用方法如下:

var sce = new Scenario();
A a1 = new A(1, sce);
A a3 = new A(3, sce);
sce.Update(2);
sce.Update(5);

这种方法确保 A 不知道 Scenario 它正在以最小的努力更新,并且非常可测试。另一种方法是在 A 上添加一个方法,该方法 注册 一个 Scenario (如果 A 需要添加一个新的 Scenario创建后):

public void Register(IScenario scenario)
{
    scenario.Add(this);
    scenario.Add(B1);
}

注意:创建 IScenario 不是 必需的,但是当您进入 unit testing 并想要 mock 个对象。

编辑: 您始终可以使用反射从 A:

获取类型为 toBeUpdated 的所有属性
// In Scenario
public void Add(toBeUpdated obj)
{
    UpdateEvent += obj.OnUpdate;

    // Get all properties of type toBeUpdated
    var properties = obj.GetType()
        .GetProperties(BindingFlags.NonPublic | BindingFlags.Public | BindingFlags.Instance | BindingFlags.Static)
        .Select(x => x.GetValue(obj))
        .OfType<toBeUpdated>();

    foreach (var property in properties)
    {
        UpdateEvent += property.OnUpdate;
    }
}