C#动态getter和setter取决于应用程序上下文

VSP*_*VSP 4 c# asp.net dll function winforms

我们有一个函数库,一些实用程序变量以两种不同的方式存储,具体取决于应用程序上下文桌面应用程序/网站

在网站中我们使用Sessions和桌面静态变量,我们希望联合并自动化这些变量的getter // setter而不会影响性能太多

例:

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;}
     }
}
Run Code Online (Sandbox Code Playgroud)

解决方案1我们认为,使用IF(对性能似乎不好,多次考虑访问变量,在某些情况下变量是具有复杂内容的自定义类):

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; }
       }
   }
}
Run Code Online (Sandbox Code Playgroud)

解决方案2使用委托,在静态类构造函数中将委托方法关联到每个get和set,具体取决于具体情况.如果webMode指向ConsWEB的get/set方法,否则指向ConsAPP的get/set方法...

解决方案2是性能最佳的解决方案吗?这种情况还有其他方法吗?

And*_*tan 6

两者都不是最佳的......

首先,先忘掉性能思维设计.

你应该通过一个接口或类似的方式来做:

public interface IConsProvider
{
  string UserName { get; set; }
}
Run Code Online (Sandbox Code Playgroud)

现在你的实现(注意:你不应该在同一个程序集中同时编译桌面和Web.例如,System.Web在Client Profile中不可用 - 你应该真正用于桌面应用程序).

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;
   }
}
Run Code Online (Sandbox Code Playgroud)

然后你的环境静态:

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;
    }
  }
}
Run Code Online (Sandbox Code Playgroud)

现在您拥有一个可扩展的提供程序,您无需担心其实现.

我个人也有一个包装问题HttpContext.Current- 但是在大多数情况下工作正常 - 但是如果你有任何异步,那么你必须要小心.

另外 - 正如我在评论中提到的那样 - 您现在不再需要将属性包装为静态Cons.实际上,通过更改这样的代码,您获得了大量的可测试性和可扩展性:

public void TraceUserName()
{
  Trace.WriteLine(Cons.UserName ?? "[none]");
}
Run Code Online (Sandbox Code Playgroud)

对此:

public void TraceUserName(IConsProvider provider)
{
  Trace.WriteLine(provider.UserName ?? "[none]");
}
Run Code Online (Sandbox Code Playgroud)

相信我会在你的代码中有些时候你会希望"只是为了这个调用我想覆盖UserName - 但我不能,因为它是一个静态属性".

最后,您现在可以使用另一种扩展性机制,而不是使用静态扩展方法.

假设您为字符串的接口添加了一个通用存储机制:

string this[string key] { get; set; }
Run Code Online (Sandbox Code Playgroud)

所以这是一个字符串索引器,允许我们为不可预见的值实现类似字典的功能.假设他们都已经实现,用Dictionary<string, string>DefaultConsProvider和包裹SessionWebConsProvider).

现在,如果我正在为您的项目编写一个额外的模块,需要一些额外的字符串值 - 我可以这样做:

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;
  }
}
Run Code Online (Sandbox Code Playgroud)

(抱歉必须更新最后一位因为某些原因我参数化了密钥 - 这是没有意义的!)

也就是说 - 我们现在可以通过扩展方法开始扩展提供者提供的强类型设置的范围 - 而无需更改任何原始代码.