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

本文关键字:初始化 静态 变量 Prism MEF 使用 | 更新日期: 2023-09-27 18:28:00

上下文

我有一个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-并保存对它的引用。例如:

[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方法时。因此,每个模块都有自己的值,在该特定模块的所有实例中共享。

我知道懒惰的评价是由于beforefieldinit的好奇心。然而,我预计整个应用程序的评估只会发生一次,而不是每个模块。

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

static int _modalsCount;
static InteractionWindowPresenter()
{
    _modalsCount = 0;
}

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

根据我的理解,static变量在每个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在干扰我?

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

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

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

根据C#语言规范,第4.4.2节打开和关闭类型:

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

你可以做一个简单的测试:

public class Test<T>
{
    public static object obj = new object();
}
Console.WriteLine(object.ReferenceEquals(Test<string>.obj, Test<object>.obj)); // false

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