1

環境

Windowsの作成を担当するInteractionWindowPresenterクラスがあります。それらのいくつかはモーダルである可能性があり、アプリケーションの他の部分に通知するために、開いているモーダル ウィンドウの数のカウンターを保持したいと考えています。

したがって_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によるものであることは理解しています。ただし、モジュールごとではなく、アプリケーション全体に対して 1 回だけ評価が行われることを期待していました。

また、静的コンストラクターで初期化を実行しようとしました。

static int _modalsCount;

static InteractionWindowPresenter()
{
    _modalsCount = 0;
}

この場合、静的コンストラクターはインスタンス コンストラクターの実行前に呼び出されますが、インスタンスが作成されるたびに呼び出されます。したがって、変数はもはや静的ではないようです。

私の理解では、static変数は ごとに 1 回初期化されますAppDomain。したがって、すべてのアセンブリ (モジュールとインフラストラクチャ) が同じ にあるためAppDomain、これは発生しないはずです。これら2つの仮定のいずれかで間違っていますか?

これまでに採用された回避策

カウンターを保持する単純なクラスを作成すると、この問題を回避できます。

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

スタティックは、タイプごとに 1 回作成されます。ジェネリック型を使用しているため、作成される型の数は、初期化子で使用する型変数の組み合わせの数と同じになります。これが、非ジェネリック クラス内に static を隠すことが機能する理由です (いずれにせよ、おそらくより良いパターンです)。

于 2015-12-23T11:42:48.387 に答える
2

クラスはジェネリックであり、構築された各ジェネリック型 (型引数が指定されている) は個別の型です。それぞれに独自の静的メンバーのセットがあります。

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

あなたの回避策 (静的カウンターを非ジェネリック クラスに保持する) は正しいです。

于 2015-12-23T11:44:42.700 に答える