1

语境

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

我知道懒惰的评估是由于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 惹恼了我?

4

2 回答 2

3

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

于 2015-12-23T11:42:48.387 回答
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

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

于 2015-12-23T11:44:42.700 回答