繁体   English   中英

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

[英]Static variable initialized more than once using Prism and MEF

语境

我有一个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();
   }
}

初始化后,每个棱镜模块 -即。 每个实现IModule类-每个必须在Window上显示的视图实例化一个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搞砸了我?

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

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

根据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

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

暂无
暂无

声明:本站的技术帖子网页,遵循CC BY-SA 4.0协议,如果您需要转载,请注明本站网址或者原文地址。任何问题请咨询:yoyou2525@163.com.

 
粤ICP备18138465号  © 2020-2024 STACKOOM.COM