4

関数ライブラリがあり、いくつかのユーティリティ変数は、アプリのコンテキストデスクトップアプリ/ウェブサイトに応じて2つの異なる方法で保存されます

ウェブサイトではSessionsを使用し、デスクトップの静的変数では、パフォーマンスにあまり影響を与えずに、これらの変数のゲッター//セッターを統合して自動化したいと考えています。

例:

public static class Cons
{
   public static bool webMode;
}

public static class ConsWEB
{
     public static string Username
     {
       get{ return HttpContext.Current.Session["username"].ToString();}
       set{ HttpContext.Current.Session["username"]=value;}
     }
}

public static class ConsAPP
{    
     private static string _username;
     public static string Username
     {
       get{ return _username;}
       set{ _username=value;}
     }
}

解決策1IFを使用して考えました(パフォーマンスに悪いようです。変数へのアクセスを何度も考慮します。場合によっては、変数は複雑な内容のカスタムクラスです)。

public static class Cons
{
   public static bool webMode;

   public static string Username
   {
       get{ return webMode? ConsWEB.Username : ConsAPP.Username; }
       set
       { 
           if(webMode) { ConsWEB.Username = value; }
           else        { ConsAPP.Username = value; }
       }
   }
}

デリゲートを使用するソリューション2では、静的クラスコンストラクターで、ケースに応じてデリゲートされたメソッドを各getおよびsetに関連付けます。webModeがConsWEBのget/setメソッドを指している場合、そうでない場合はConsAPPのget/setメソッドを指します。

ソリューション2は、パフォーマンスの面で最高のものですか?この場合の他の方法論はありますか?

4

1 に答える 1

6

どちらも最適ではありません...

まず、パフォーマンスを忘れて、最初にデザインを考えます。

インターフェイスなどを介して実行する必要があります。

public interface IConsProvider
{
  string UserName { get; set; }
}

これで実装が完了しました(注:同じアセンブリでデスクトップとWebの両方をコンパイルする必要はありません。たとえば、System.Webはクライアントプロファイルでは使用できません。デスクトップアプリで実際に使用する必要があります)。

public class WebConsProvider : IConsProvider
{
  public string UserName
  {
    // DON'T USE .ToString()!  If it's null you get NullReferenceException!
    get{ return HttpContext.Current.Session["username"] as string; }
    set{ HttpContext.Current.Session["username"]=value; }
  }
}

public class DefaultConsProvider : IConsProvider 
{
   public string UserName 
   {
     get; set;
   }
}

そして、あなたの環境は静的です:

public static class Cons 
{
  //initialise to default as well - only web apps need change it
  private static IConsProvider _provider = new DefaultConsProvider();
  public static IConsProvider Provider 
  {
    get { return _provider; }
    set { _provider = value; /* should check for null here and throw */ }
  }

  //if you really want you can then wrap the properties
  public static string UserName 
  {
    get {
      return _provider.UserName;
    }
    set {
      _provider.UserName = value;
    }
  }
}

これで、実装について心配する必要のない拡張可能なプロバイダーができました。

個人的にはラッピングにも問題がありますがHttpContext.Current、ほとんどのシナリオでは正常に機能しますが、非同期が発生している場合は注意が必要です。

また、コメントで述べたように、プロパティを静的なものとしてラップする必要がなくなりましたCons。実際、次のようにコードを変更することで、非常に多くのテスト容易性と拡張性が得られます。

public void TraceUserName()
{
  Trace.WriteLine(Cons.UserName ?? "[none]");
}

これに:

public void TraceUserName(IConsProvider provider)
{
  Trace.WriteLine(provider.UserName ?? "[none]");
}

コード内で、「この呼び出しのためだけにUserNameをオーバーライドしたいのですが、静的プロパティであるため、オーバーライドできません」と思うことがあると思います

最後に、静的ではない別の拡張メカニズムを自由に使用できます。拡張メソッドです。

文字列のインターフェイスに共通のストレージメカニズムを追加するとします。

string this[string key] { get; set; }

これが文字列インデクサーであり、予期しない値に対して辞書のような機能を実装できるようにします。Dictionary<string, string>がで、がDefaultConsProviderでラップSessionされて、両方が実装されていると仮定しますWebConsProvider)。

これで、追加の文字列値を必要とするプロジェクト用の追加モジュールを作成している場合、これを行うことができます。

public static MySettingsExtensions 
{
  public static string GetMySetting(this IConsProvider provider) 
  {
    //TODO: argument null checks
    return provider["MySetting"];
  }

  public static void SetMySetting(this IConsProvider provider, string val) 
  {
    provider["MySetting"]=val;
  }
}

(申し訳ありませんが、何らかの理由でキーをパラメーター化したため、最後のビットを更新する必要がありました-これは無意味でした!)

つまり、元のコードを変更することなく、プロバイダーが提供する厳密に型指定された設定の範囲を拡張メソッドを介して拡張できるようになりました。

于 2012-11-21T12:00:25.967 に答える