如何在不知道调用者 class 的情况下订阅对象?
How to subscribe objects without knowing their invoker's class?
我有一个 Scenario,它使用 A 或几个 As。 A 是在 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;
}
}
我有一个 Scenario,它使用 A 或几个 As。 A 是在 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;
}
}