静态变量使用Prism和MEF初始化多次

时间:2015-12-23 11:30:57

标签: c# mef composite-application prism-6

上下文

我有一个InteractionWindowPresenter班负责创建Windows。其中一些可能是模态的,我想保留一个打开的模态窗口数的计数器,以通知应用程序的其他部分。

因此我在类中添加了_modalsCount变量,每当打开或关闭模态窗口时都会更新:

public class InteractionWindowPresenter<TWindow, TNotification>
    where TWindow : System.Windows.Window
    where TNotification : Prism.Interactivity.InteractionRequest.INotification
{
   private static int _modalsCount = 0;

   ...

   private bool _useModalWindow;

   public InteractionWindowPresenter(InteractionRequest<TNotification> request, 
      bool useModalWindow = false)
   {
      _useModalWindow = useModalWindow;
   }

   public void Show()
   {
      var window = ...

      window.Closed += (s, e) =>
      {
         if (_useModalWindow)
         {
             _modalsCount = Math.Max(0, --_modalsCount);

             if (_modalsCount == 0)
                 ServiceLocator.Current.GetInstance<IEventAggregator>()
                    .GetEvent<ModalStatusChanged>().Publish(false);
         }       
      };

      if (_useModalWindow)
      {
         _modalsCount++;

         ServiceLocator.Current.GetInstance<IEventAggregator>()
            .GetEvent<ModalStatusChanged>().Publish(true);

         window.ShowDialog();
      }
      else
         window.Show();
   }
}

初始化后,每个 Prism模块 - 即。实现IModule的每个类 - 为每个视图实例化InteractionWindowPresenter,必须在Window上显示并保存对它的引用。例如:

[ModuleExport("ClientsModule", typeof(Module), 
    DependsOnModuleNames = new[] { "RibbonModule", "ClientsModelModule" }, 
    InitializationMode = InitializationMode.WhenAvailable)]
public class Module : IModule
{
    InteractionWindowPresenter<ClientSelectionWindow, ClientSelection> _selectionPresenter;

    public void Initialize()
    {
       _selectionPresenter = 
           new InteractionWindowPresenter<ClientSelectionWindow, ClientSelection>
              (Interactions.ClientSelectionRequest, useModalWindow: true);
    }
}

InteractionWindowPresenter类在由所有模块以及其他基础结构程序集直接引用的基础结构程序集中定义。启动器应用程序不会引用它,它只是MefBootstrapper。因此, MEF 用于构图。

问题

_modalsCount初始化行上设置断点显示在创建InteractionWindowPresenter实例时不会执行该断点。相反,它是在第一次(并且只是那个时间)执行变量在每个模块中使用 - 即。第一次从每个模块调用Show方法。因此,每个模块都有自己的值,在该特定模块的所有实例之间共享。

我知道懒惰的评估归因于the curious nature of beforefieldinit。但是我希望评估只针对整个应用程序而不是每个模块进行一次。

我还尝试在静态构造函数中执行初始化:

static int _modalsCount;

static InteractionWindowPresenter()
{
    _modalsCount = 0;
}

在这种情况下,在执行实例构造函数之前调用静态构造函数,但每次都会创建一个实例。因此,变量似乎不再是静态的。

据我所知,static variables are initialized once per AppDomain。因此,由于我的所有程序集(模块和基础结构)都在同一AppDomain,因此不应该发生这种情况。在这两个假设中,我错了吗?

到目前为止已采用的解决方法

创建一个简单的类来保存计数器可以避免这个问题:

static class ModalsCounter
{
    private static int _modalsCount = 0;

    public static int Increment()
    {
        return ++_modalsCount;
    }

    public static int Decrement()
    {
        _modalsCount = Math.Max(0, --_modalsCount);
        return _modalsCount;
    }
}

因此将调用替换为_modalsCount

ModalsCounter.Increment();

ServiceLocator.Current.GetInstance<IEventAggregator>()
   .GetEvent<ModalStatusChanged>().Publish(true);

if (_useModalWindow && ModalsCounter.Decrement() == 0)
    ServiceLocator.Current.GetInstance<IEventAggregator>()                    
      .GetEvent<ModalStatusChanged>().Publish(false);

那我在这里错过了什么?我是否有点误解了静态变量的生命周期和范围,还是Prism模块和/或MEF弄乱了我?

2 个答案:

答案 0 :(得分:3)

为每个Type创建一次静态。由于您使用的是通用类型,因此创建的类型数将等于您在初始化程序中使用的类型变量组合的数量。这就是为什么隐藏非泛型类中的静态工作(无论如何可能是一个更好的模式)。

答案 1 :(得分:2)

您的类是通用的,每个构造的泛型类型(指定了类型参数)是一个单独的类型。他们每个人都有自己的一组静态成员。

来自C# language specification,第4.4.2节打开和关闭类型:

  

每个封闭的构造类型都有自己的一组静态变量,这些变量不与任何其他封闭的构造类型共享。由于在运行时不存在打开类型,因此没有与打开类型关联的静态变量。

您可以进行简单的测试:

public class Test<T>
{
    public static object obj = new object();
}

Console.WriteLine(object.ReferenceEquals(Test<string>.obj, Test<object>.obj)); // false

您的解决方法(将静态计数器保留在非泛型类中)是正确的。