4

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

在网站中,我们使用 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;}
     }
}

我们认为的解决方案 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; }
       }
   }
}

解决方案 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 - 但我不能,因为它是一个静态属性”

最后,您现在可以使用另一种可扩展性机制,而 statics 则没有:扩展方法。

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

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

这是一个字符串索引器,允许我们为不可预见的值实现类似字典的功能。假设它们都已被实现,其中 a 包含在Dictionary<string, string>)中。DefaultConsProviderSessionWebConsProvider

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

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 回答