C# 类型特定的观察者
C# type specific observers
在此应用程序中,观察者处理对象网络中的状态变化。所有对象都是基于相同的 BaseObject class 派生的 classes。 BaseObject 提供基本的识别和导航功能。
从 BaseObject 派生的 类 是由代码生成器创建的。这些 classes 应具有最小的足迹,并专注于特定的状态和行为。
在应用程序级别,观察者处理 BaseObject 派生的 classes 中的状态变化,通常不止一个 class,而且主要是针对大量 BaseObject 派生的实例。
在当前解决方案中,BaseObject 管理观察者并以 BaseObject 实例作为发送者通知观察者。
using System;
using System.Collections.Generic;
namespace Observer {
#region underlying framework foundation
interface IObserver {
void ObjectChanged (BaseObject obj);
}
abstract class BaseObject {
HashSet<IObserver> observers = new HashSet<IObserver> ();
public void RegisterObserver (IObserver observer) {
observers.Add (observer);
}
public void FireObjectChanged () {
foreach (var obs in observers)
obs.ObjectChanged (this);
}
}
#endregion underlying framework foundation
#region code generator
class DerivedObject1 : BaseObject {
}
class DerivedObject2 : BaseObject {
}
#endregion code generator
#region application code
class Observer : IObserver {
public void ObjectChanged (BaseObject obj) {
Console.WriteLine (obj.GetType ().Name);
if (obj is DerivedObject1) {
} else if (obj is DerivedObject2) {
}
}
}
#endregion application code
#region sample
class Program {
static void Main (string[] args) {
Observer observer = new Observer ();
List<BaseObject> objects = new List<BaseObject> ();
DerivedObject1 obj1 = new DerivedObject1 ();
objects.Add (obj1);
obj1.RegisterObserver (observer);
DerivedObject2 obj2 = new DerivedObject2 ();
objects.Add (obj2);
obj2.RegisterObserver (observer);
foreach (var bo in objects)
bo.FireObjectChanged ();
}
}
#endregion sample
}
我不喜欢这种方法的地方是观察者必须在 运行 时识别发送者类型。相反,我想让它在编译时是安全的。
因此我提出了一种引入泛型和第二个 BaseObject 层的新方法,BaseObjectT 包含类型安全的观察者。
using System;
using System.Collections.Generic;
namespace Observer {
#region underlying framework foundation
interface IObserver<T> where T : BaseObjectT<T> {
void ObjectChanged (T obj);
}
abstract class BaseObject {
public abstract void FireObjectChanged ();
}
abstract class BaseObjectT<T> : BaseObject where T : BaseObjectT<T> {
HashSet<IObserver<T>> observers = new HashSet<IObserver<T>> ();
public void RegisterObserver (IObserver<T> observer) {
observers.Add (observer);
}
public override void FireObjectChanged () {
foreach (var obs in observers)
obs.ObjectChanged ((T)this);
}
}
#endregion underlying framework foundation
#region code generator
class DerivedObject1 : BaseObjectT<DerivedObject1> {
}
class DerivedObject2 : BaseObjectT<DerivedObject2> {
}
#endregion code generator
#region application code
class Observer :
IObserver<DerivedObject1>,
IObserver<DerivedObject2> {
public void ObjectChanged (DerivedObject1 obj) {
Console.WriteLine (obj.GetType ().Name);
}
public void ObjectChanged (DerivedObject2 obj) {
Console.WriteLine (obj.GetType ().Name);
}
}
#endregion application code
#region sample
class Program {
static void Main (string[] args) {
Observer observer = new Observer ();
List<BaseObject> objects = new List<BaseObject> ();
DerivedObject1 obj1 = new DerivedObject1 ();
objects.Add (obj1);
obj1.RegisterObserver (observer);
DerivedObject2 obj2 = new DerivedObject2 ();
objects.Add (obj2);
obj2.RegisterObserver (observer);
foreach (var bo in objects)
bo.FireObjectChanged ();
}
}
#endregion sample
}
虽然这种方法有效并且在应用程序级别完全符合我的要求 - 将派生的实例 class 传递给观察者而不需要在那里进行类型转换,并允许重载不同的 IObserver方法实现 - 在底层对我来说似乎有点难看。
我现在的问题是,是否有更好、更优雅的方法来完成此操作,特别是有没有办法避免在 FireObjectChanged() 中进行强制转换
obs.ObjectChanged ((T)this);
或者将 BaseObject 和 BaseObjectT 组合成一个基 class?
正如@Falanwe 评论的那样,IObserver<T>
是一个系统class,你应该给你的观察者起别的名字。我为下面的示例代码选择了 ICustomObserver<T>
。
我想到的唯一选择就是这个。我们创建了一个扩展方法来将委托存储在 ConditionalWeakTable 中。这些委托 (Action<BaseObject>
) 可以在每个对象的基础上从另一个扩展方法中调用 ObjectChanged。
这种方法的优点是您的 BaseObject 中不需要任何花哨的东西,并且您可以以类型安全的方式为 BaseObject 或 DerivedObject1 调用 RegisterObserver。
缺点是如果您还不了解扩展方法、委托或 ConditionalWeakTable,则可能会有一个学习曲线。
public static class ObjectChangedExtension
{
internal static ConditionalWeakTable<object, List<Action<BaseObject>>> observers
= new ConditionalWeakTable<object, List<Action<BaseObject>>>();
public static void RegisterObserver<T>(this T obj, ICustomObserver<T> observer)
where T : BaseObject
{
Action<BaseObject> objChangedDelegate = v => observer.ObjectChanged((T)v);
observers
.GetOrCreateValue(obj)
.Add(objChangedDelegate);
}
public static void FireObjectChanged(this BaseObject obj)
{
observers
.GetOrCreateValue(obj)
.ForEach(v => v(obj));
}
}
#region code generator
class DerivedObject1 : BaseObject
{
}
class DerivedObject2 : BaseObject
{
}
#endregion code generator
#region application code
class Observer : ICustomObserver<DerivedObject1>, ICustomObserver<DerivedObject2>
{
public void ObjectChanged(DerivedObject1 obj)
{
Console.WriteLine("DerivedObject1 Observer");
}
public void ObjectChanged(DerivedObject2 obj)
{
Console.WriteLine("DerivedObject2 Observer");
}
}
class ObserverOfBase : ICustomObserver<BaseObject>
{
public void ObjectChanged(BaseObject obj)
{
Console.WriteLine("BaseObject Observer");
}
}
#endregion application code
#region sample
class Program
{
internal static void Main(string[] args)
{
Observer observer = new Observer();
List<BaseObject> objects = new List<BaseObject>();
DerivedObject1 obj1 = new DerivedObject1();
objects.Add(obj1);
obj1.RegisterObserver(observer);
DerivedObject2 obj2 = new DerivedObject2();
objects.Add(obj2);
obj2.RegisterObserver(observer);
var baseObjectObserver = new ObserverOfBase();
obj1.RegisterObserver(baseObjectObserver);
obj2.RegisterObserver(baseObjectObserver);
foreach (var bo in objects)
bo.FireObjectChanged();
}
}
#endregion sample
public class BaseObject
{
}
public interface ICustomObserver<T>
{
void ObjectChanged(T obj);
}
在此应用程序中,观察者处理对象网络中的状态变化。所有对象都是基于相同的 BaseObject class 派生的 classes。 BaseObject 提供基本的识别和导航功能。
从 BaseObject 派生的类 是由代码生成器创建的。这些 classes 应具有最小的足迹,并专注于特定的状态和行为。
在应用程序级别,观察者处理 BaseObject 派生的 classes 中的状态变化,通常不止一个 class,而且主要是针对大量 BaseObject 派生的实例。
在当前解决方案中,BaseObject 管理观察者并以 BaseObject 实例作为发送者通知观察者。
using System;
using System.Collections.Generic;
namespace Observer {
#region underlying framework foundation
interface IObserver {
void ObjectChanged (BaseObject obj);
}
abstract class BaseObject {
HashSet<IObserver> observers = new HashSet<IObserver> ();
public void RegisterObserver (IObserver observer) {
observers.Add (observer);
}
public void FireObjectChanged () {
foreach (var obs in observers)
obs.ObjectChanged (this);
}
}
#endregion underlying framework foundation
#region code generator
class DerivedObject1 : BaseObject {
}
class DerivedObject2 : BaseObject {
}
#endregion code generator
#region application code
class Observer : IObserver {
public void ObjectChanged (BaseObject obj) {
Console.WriteLine (obj.GetType ().Name);
if (obj is DerivedObject1) {
} else if (obj is DerivedObject2) {
}
}
}
#endregion application code
#region sample
class Program {
static void Main (string[] args) {
Observer observer = new Observer ();
List<BaseObject> objects = new List<BaseObject> ();
DerivedObject1 obj1 = new DerivedObject1 ();
objects.Add (obj1);
obj1.RegisterObserver (observer);
DerivedObject2 obj2 = new DerivedObject2 ();
objects.Add (obj2);
obj2.RegisterObserver (observer);
foreach (var bo in objects)
bo.FireObjectChanged ();
}
}
#endregion sample
}
我不喜欢这种方法的地方是观察者必须在 运行 时识别发送者类型。相反,我想让它在编译时是安全的。
因此我提出了一种引入泛型和第二个 BaseObject 层的新方法,BaseObjectT 包含类型安全的观察者。
using System;
using System.Collections.Generic;
namespace Observer {
#region underlying framework foundation
interface IObserver<T> where T : BaseObjectT<T> {
void ObjectChanged (T obj);
}
abstract class BaseObject {
public abstract void FireObjectChanged ();
}
abstract class BaseObjectT<T> : BaseObject where T : BaseObjectT<T> {
HashSet<IObserver<T>> observers = new HashSet<IObserver<T>> ();
public void RegisterObserver (IObserver<T> observer) {
observers.Add (observer);
}
public override void FireObjectChanged () {
foreach (var obs in observers)
obs.ObjectChanged ((T)this);
}
}
#endregion underlying framework foundation
#region code generator
class DerivedObject1 : BaseObjectT<DerivedObject1> {
}
class DerivedObject2 : BaseObjectT<DerivedObject2> {
}
#endregion code generator
#region application code
class Observer :
IObserver<DerivedObject1>,
IObserver<DerivedObject2> {
public void ObjectChanged (DerivedObject1 obj) {
Console.WriteLine (obj.GetType ().Name);
}
public void ObjectChanged (DerivedObject2 obj) {
Console.WriteLine (obj.GetType ().Name);
}
}
#endregion application code
#region sample
class Program {
static void Main (string[] args) {
Observer observer = new Observer ();
List<BaseObject> objects = new List<BaseObject> ();
DerivedObject1 obj1 = new DerivedObject1 ();
objects.Add (obj1);
obj1.RegisterObserver (observer);
DerivedObject2 obj2 = new DerivedObject2 ();
objects.Add (obj2);
obj2.RegisterObserver (observer);
foreach (var bo in objects)
bo.FireObjectChanged ();
}
}
#endregion sample
}
虽然这种方法有效并且在应用程序级别完全符合我的要求 - 将派生的实例 class 传递给观察者而不需要在那里进行类型转换,并允许重载不同的 IObserver方法实现 - 在底层对我来说似乎有点难看。
我现在的问题是,是否有更好、更优雅的方法来完成此操作,特别是有没有办法避免在 FireObjectChanged() 中进行强制转换
obs.ObjectChanged ((T)this);
或者将 BaseObject 和 BaseObjectT 组合成一个基 class?
正如@Falanwe 评论的那样,IObserver<T>
是一个系统class,你应该给你的观察者起别的名字。我为下面的示例代码选择了 ICustomObserver<T>
。
我想到的唯一选择就是这个。我们创建了一个扩展方法来将委托存储在 ConditionalWeakTable 中。这些委托 (Action<BaseObject>
) 可以在每个对象的基础上从另一个扩展方法中调用 ObjectChanged。
这种方法的优点是您的 BaseObject 中不需要任何花哨的东西,并且您可以以类型安全的方式为 BaseObject 或 DerivedObject1 调用 RegisterObserver。
缺点是如果您还不了解扩展方法、委托或 ConditionalWeakTable,则可能会有一个学习曲线。
public static class ObjectChangedExtension
{
internal static ConditionalWeakTable<object, List<Action<BaseObject>>> observers
= new ConditionalWeakTable<object, List<Action<BaseObject>>>();
public static void RegisterObserver<T>(this T obj, ICustomObserver<T> observer)
where T : BaseObject
{
Action<BaseObject> objChangedDelegate = v => observer.ObjectChanged((T)v);
observers
.GetOrCreateValue(obj)
.Add(objChangedDelegate);
}
public static void FireObjectChanged(this BaseObject obj)
{
observers
.GetOrCreateValue(obj)
.ForEach(v => v(obj));
}
}
#region code generator
class DerivedObject1 : BaseObject
{
}
class DerivedObject2 : BaseObject
{
}
#endregion code generator
#region application code
class Observer : ICustomObserver<DerivedObject1>, ICustomObserver<DerivedObject2>
{
public void ObjectChanged(DerivedObject1 obj)
{
Console.WriteLine("DerivedObject1 Observer");
}
public void ObjectChanged(DerivedObject2 obj)
{
Console.WriteLine("DerivedObject2 Observer");
}
}
class ObserverOfBase : ICustomObserver<BaseObject>
{
public void ObjectChanged(BaseObject obj)
{
Console.WriteLine("BaseObject Observer");
}
}
#endregion application code
#region sample
class Program
{
internal static void Main(string[] args)
{
Observer observer = new Observer();
List<BaseObject> objects = new List<BaseObject>();
DerivedObject1 obj1 = new DerivedObject1();
objects.Add(obj1);
obj1.RegisterObserver(observer);
DerivedObject2 obj2 = new DerivedObject2();
objects.Add(obj2);
obj2.RegisterObserver(observer);
var baseObjectObserver = new ObserverOfBase();
obj1.RegisterObserver(baseObjectObserver);
obj2.RegisterObserver(baseObjectObserver);
foreach (var bo in objects)
bo.FireObjectChanged();
}
}
#endregion sample
public class BaseObject
{
}
public interface ICustomObserver<T>
{
void ObjectChanged(T obj);
}