2015-04-03 81 views
1

在此应用程序中,观察者处理对象网络中的状态更改。所有对象都是基于相同BaseObject类的派生类。 BaseObject提供重要的识别和导航功能。C#类型特定观察者

从BaseObject派生的类由代码生成器创建。这些类应具有最小的占用空间,并专注于特定的状态和行为。

在应用程序级别,观察者处理BaseObject派生类中的状态更改,通常对于多个类而言,并且通常对于大量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层,它包含类型安全的观察者。

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 
} 

虽然这种方法,准确地我想要做什么在应用层面 - 通过派生类的实例,以观察者无需类型强制转换那里,不同IObserver方法实现的,允许超载 - 在底层我看起来有点难看。

我现在的问题,是有没有更好的,来完成这个更优雅的方式,特别是有没有办法避免FireObjectChanged)演员(

obs.ObjectChanged ((T)this);  

,或者BaseObject和BaseObjectT结合成单个基类?

+0

你不应该命名的东西'IObserver '除非,以及['IObserver '](https://msdn.microsoft.com/en-us/library/dd783449(V = VS。 110).aspx) – Falanwe 2015-04-03 10:31:55

回答

0

作为@Falanwe评论,IObserver<T>是一个系统类,你应该呼吁你的观察员别的东西。我选择了ICustomObserver<T>作为下面的示例代码。

我想出的唯一选择是这个。我们创建一个扩展方法来将代表存储在一个有条件的WeakTable中。这些代表(Action<BaseObject>)可以在每个对象的基础上从另一个扩展方法内调用ObjectChanged。

这种方法的优点是,您不需要任何花费在您的BaseObject中,并且可以以类型安全的方式为BaseObject或DerivedObject1调用RegisterObserver。

缺点是,如果您尚不了解扩展方法,委托或条件关系表,那么可能存在学习曲线。

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); 
} 
+0

有问题的框架最初是在C#2.0下实现的,并且当时严格地基于接口(当时IObserver不是系统类型)。与此同时,有不少代表机制渗透进来,扩展方法在这里和那里用来增加功能。 – geophil 2015-04-04 10:06:27

+0

我已经针对您的建议针对10,000个对象和10,000个触发器执行了性能测试。它实际上比我的多态呼叫快一点。但是我可能会将'ConditionalWeakTable'替换为传统'Dictionary'作为'BaseObject'或'Observer'生命周期在这里无关紧要,性能测试通过经典字典给出了更好的结果。非常感谢您的帮助。 – geophil 2015-04-04 10:13:46

+0

此外,扩展方法的优点是代码生成器可以保持不变,并且唯一且不会中断的更改必须在'BaseObject'中进行,以包含新的类型特定的观察器。这将有助于顺利迁移。 – geophil 2015-04-04 10:21:28