【问题标题】:C# non-persistent `static` keyword?C#非持久性`static`关键字?
【发布时间】:2009-09-08 15:05:06
【问题描述】:

我来自 C/C++ 背景,在使用 C# 做一些事情时遇到了麻烦。我现在的问题是我需要一个在 C++ 中工作的 static 关键字。这样该属性对于类的所有实例都是全局的,这就是 C# 所做的。但我不想要的是 C#(/ASP.NET) 中的持久性。我希望静态属性仅对当前执行页面上的类的所有实例都是全局的。

这怎么可能?

基本上,我使用它是唯一的命名,它只能在当前执行的页面上是唯一的。如果静态属性是持久的,那么在高流量期间整数翻转的可能性会更高,这可能导致相同的值被使用两次。

如果有人去一个页面

static int i=0;
page_load...{
  lbl.Text=i;
  i++;
}

那么他们将得到 0。如果其他人访问同一页面,他们也会得到 0。但也具有静态属性,因此它在类的所有实例中都是相同的。

【问题讨论】:

  • 简单的解决方案是使用 long。 64 位值不会很快翻转。老实说,我也怀疑 32 位整数。那仍然是 40 亿个请求。我怀疑您的网站在平均会话期间会产生那么多访问量。
  • 可能是真的.. 但假设“哦,我们永远不会有那么多人”是不好的,因为如果每个页面请求这个数字可能会增加 100 次或更多次,这很可能会发生
  • 无论如何你都在假设。即使该数字是每页的,如果该页面的请求足够频繁,它也会溢出。诀窍是做出明智的假设。无论页面被请求的频率如何,64 位整数都不会溢出。你可以每纳秒递增一次,它不会在 584 年内回绕。
  • 好吧...如果您每秒获得 1000 次点击(阅读:实际上可以在单个 appdomain 中提供这么多的点击!),那么一个有符号的 32 位整数将在 23 个不间断的天后溢出加载。现在,我可以想象 64 位值只是更简单的路线,但是如果您正在寻找一个在平均会话长度内有效的唯一 ID,如果您知道限制,无论负载如何,你不会遇到麻烦。有时值得节省一些东西;例如,如果您需要将值粘贴到 url 中。
  • 另外,我正在寻找一种方法来计算递归函数中的递归(跨单独的实例)以防止溢出。如果你使用静态并且页面崩溃,你现在有一个高于它应该是的递归级别 10-20(尽管堆栈实际上是空的),它可以在生产环境中玩 havok

标签: c# asp.net static unique


【解决方案1】:

不幸的是,在这种情况下,您将无法获得想要的结果,因为您试图将两个不兼容的概念结合起来

  • static 是 C# 中的修饰符,可将成员(方法、属性、字段等)从绑定到实例中释放出来。它是更准确地绑定到 AppDomain 中的类型的实例。
  • 特定于当前执行页面的计数器必须与实例相关联

我认为您最好的选择是创建一个实例级属性并执行传递实例所需的操作并更新计数器。

但是我对你的情况还是有点不清楚。看来您可能想要以下任何一种...

  • 特定于当前执行页面实例的属性
  • 特定于执行页面的所有实例的属性
  • 特定于当前会话中所有页面使用的属性

你能澄清一下是哪一个问题吗?

【讨论】:

  • 我不认为他可以使用任何特定的关键字来做到这一点,但我相信我提供了正确的解决方案。
【解决方案2】:

没有真正干净的方法可以做到这一点,但如果您的对象将始终存在于 HTTP 页面/请求的上下文中,您可以使用当前 HttpContextItems collection

public class YourClass
{
    private static readonly string _itemKey = "Key Goes Here";
    private readonly HttpContext _context;

    public YourClass()
    {
        _context = HttpContext.Current;
        if (_context == null) throw new InvalidOperationException("Boom!");

        // set a default value here if you like...
        if (_context.Items[_itemKey] == null)
        {
            _context.Items[_itemKey] = new YourType("Default Value");
        }
    }

    public YourType NonPersistentStatic
    {
        get { return (YourType)(_context.Items[_itemKey]); }
        set { _context.Items[_itemKey] = value; }
    }
}

【讨论】:

  • 我认为我们都将这个作为答案确定下来,如果他想在每个会话或每个请求中存在,这只是发布者的偏好。我认为 session 更有意义,因为为什么它只需要对请求是静态的,因为它可以由传递的实例对象或注入的依赖项来处理。
【解决方案3】:

我不确定我是否 100% 理解您的问题......

我相信您真正想要的是在 Session 中存储一个对象。

我会做这样的事情

public class PageBase : Page
{

    public MyObject Foo 
    {
        get { return (MyObject)Session["myobject"]; }
        set { Session["myobject"] = value; }
    }
}

并将您的页面更改为从 PageBase 继承

编辑: 在 ASP.NET 中查找 [ThreadStatic] 的含义后,我看到了 Scott Hanselmen 的这篇博文,他说,不不不! A tale of two techniques: The [ThreadStatic] Attribute and System.Web.HttpContext.Current.Items。他还指出了 HttpContext 的存在,它有一个可以存储对象的内置字典。我完全忘记了这一点,主要是因为到目前为止我从来没有发现任何有用的东西。

【讨论】:

  • Session 在不同的请求中持续存在(对于同一用户)。
  • @Luke:没错,但字里行间……这可能是原始发帖人想要的。
  • 不,不是......虽然每个用户它不应该溢出(因为肯定有人不能在一天 8 小时内生成超过 10,000 个页面请求(假设它们足够活跃以阻止会话垂死)
【解决方案4】:

您从哪里引用“静态”?如果只是子控件,我会在您的页面类中添加一个常规实例属性,然后通过子控件上的页面属性引用该变量。

public class DefaultPage : Page {
    public string MyProperty { get; set; }
} 

public class DefaultControl : UserControl {
    //...
    ((DefaultPage)this.Page).MyProperty
    //...
}

老实说,如果我看到这段代码,我会认为设计有点倒退。页面应该为用户控件提供它需要的所有数据。

【讨论】:

    【解决方案5】:

    您要求的是一个 ThreadStatic 变量,但这不是一个好的解决方案。它会起作用,但它不是处理它的可靠方法。例如,ThreadStatic 变量仅针对第一个线程进行初始化,因此您需要自己处理每个线程的初始化。

    首先,您应该研究为每个元素提供唯一 ID 的内置功能。例如,如果您有一个中继器以重复相同的控件,则每次迭代都会获得一个唯一的 id,该 id 将附加在控件 id 之前。您可以使用 ClientID 属性来获取此唯一 ID。

    如果您需要自己处理命名,您应该使用 Page 类中的成员变量来跟踪命名。您可以在类的构造函数中轻松初始化变量,并且由于该变量在此之前不存在,因此您可以确定在初始化之前您不会意外使用它。

    【讨论】:

      【解决方案6】:

      不知道你为什么需要它。您可以将会话独有的内容存储在 HTTPContext.Current.Session 中。您可以在 HTTPContext.Current.ViewState 中存储对请求唯一的内容。

      要执行您想做的事情,您需要声明一个线程安全对象的应用程序范围变量。下面的示例将为您提供所有页面的唯一编号。

      1) 创建一个线程安全的计数器类,因为应用程序范围的变量默认不是线程安全的

      public class ThreadSafeCounter
      {
        private object _padlock = new object;
        private int _counterValue = 0;
      
        public int GetNextValue()
        {
           int temp;
           lock(_padlock)
           {
             _counter++;
             temp = _counter;
           }
           return temp;
        }
      
      }
      

      2) 在 Global.asax Application_Start 事件中创建应用程序变量

      HttpContext.Current.Application["MyCounter"] = new ThreadSafeCounter();
      

      3) 在您的页面代码中访问它

      var counter = HttpContext.Current.Application["MyCounter"] as ThreadSafeCounter;
      if (counter != null)
      {
        var uniqueValue = counter.GetNextValue();
        // DO SOME WORK HERE
      }
      else
      {
        //throw some exception here
      }
      

      一个潜在的陷阱: 如果您在服务器场中部署它或者您有超过 1 个工作进程,请确保您的会话模式是基于状态服务器或 SQL Server 的,因为如果它是 InProc,则处理请求的每个服务器/进程都有自己的柜台。

      【讨论】:

        猜你喜欢
        • 2019-05-26
        • 2011-04-21
        • 2016-11-04
        • 1970-01-01
        • 2021-05-15
        • 1970-01-01
        • 1970-01-01
        • 1970-01-01
        • 1970-01-01
        相关资源
        最近更新 更多