【问题标题】:Benefit of using static methods in web application在 Web 应用程序中使用静态方法的好处
【发布时间】:2011-09-07 17:51:13
【问题描述】:

我注意到在我们用于很多网页的自定义基类中,我们有一些静态方法。由于该类被用作网页的基类,因此将方法设为静态有什么好处?我什么都没想,只是想验证一下。

此外,在自定义基类中,我们有一些属性可以调用另一个类库中我们的管理器类中的其他静态方法,并返回 DataTable 或 HashTable。 我可以看到哪里是开发人员编写代码的便利因素,但除此之外,他们还有什么理由让方法在那里静态化?

所以现有的代码看起来像这样:

public class CustomBaseClass
    protected Hashtable displayText
        {
            get{
                if(_displayText == null)
                    displayText = MyManager.GetCustomersList(sCustID);

由于 GetCustomersList 是静态的,因此该方法中的每个方法也必须是静态的。一直到我们的数据访问层。以这种方式编码对我来说似乎很奇怪,但对你们所有人的想法感到好奇。

为我们的应用程序编写代码的老开发人员已经走了,他们到处使用它们。他们是否有任何负面因素或注意使用静态方法,尤其是在 asp.net 应用程序中?

如果我创建一个单例不是更有意义,那么我就不必将所有方法调用都直接调用到我们的 DAL 静态?哈哈

【问题讨论】:

  • 在使用静态方法时考虑可能的线程问题,尤其是当您谈论的是 Web 应用程序时。
  • 就像我启动一个新线程并在新线程中调用那些相同的静态方法一样?这可能是一个问题?还有其他要找的吗?

标签: c# asp.net


【解决方案1】:

通常将方法设为静态的主要原因是它不依赖于类的任何实例成员。避免了必须创建类的新实例来调用方法,然后需要在以后进行垃圾收集。

静态方法和实例方法显然都有它们的位置。通常,我为实用方法创建静态方法,这些方法从静态成员(当然您必须同步)或参数中获取所有状态,或者为类的所有实例设置类范围的属性。

您可以使用单例(尽管有些人讨厌它们),或者您可以只在最高级别的类中创建您的 DAO 对象并进一步向下注入,当然。

使用静态方法的主要问题是,虽然对静态方法进行单元测试非常容易,但模拟对静态 DAO 类的调用的结果可能会更加困难。如果它是一个注入的实例,它会更容易模拟。

【讨论】:

  • 由于这是 Web 应用程序中的基类,并且我在其中创建了 DAO 对象的实例,因此当新用户访问我的 Web 应用程序时,他会启动另一组实例,对吗?在这种情况下使用单例不是更有意义吗?
  • @DotNetDude:你可以,只要确保有一种方法可以为单元测试注入测试模拟以实现全面覆盖。
  • @DotNetDude:最简单的方法之一是让你的单例实例属性调用像 Func 这样的生成器,并在字段初始化器中默认生成器,然后有一个静态属性要更改它用于测试用例到 ISomeDao 的模拟实现。当然,这只是在您关心最大化代码覆盖率的情况下。
【解决方案2】:

在 Web 应用程序(和这个问题)的上下文中标记 static 的唯一原因是它可以在所有请求线程之间共享并保留在内存中。这意味着如果两个请求都在处理并且每个请求都尝试设置静态数据,那么您就会遇到竞争条件(以及潜在的线程问题)。

在您的特定情况下,您的老开发人员似乎只是懒惰;它没有理由必须是静态的(基于提供的代码示例)。

【讨论】:

  • 您好 Tejs,您对从管理器类创建单例并以这种方式调用方法有何感想?在 DAL 中看到静态方法调用只是让我感到厌烦,因为大多数 DAL 代码没有静态方法。
  • 班级是否需要单身人士?当您只想创建一个实例时,您会使用单例,而不是为了方便将静态方法转换为实例方法。
  • 不,我会重构管理器类,使其根本不使用任何静态方法,并使其成为单例,因此只有一个管理器类副本可用。所以我想知道,既然它是一个网络应用程序,任何点击从我的自定义基类继承的网页的新用户都会启动另一个单例实例,还是会在所有用户之间共享?
  • 如果它是一个真正的单例,那么只会创建一个实例。
【解决方案3】:

比单例或静态类更好的选择是使用Monostate Pattern

基本上,在单态模式中,通过将所有静态成员标记为私有并提供公共实例包装器,您可以为静态类或单例提供普通类的语义。这是一种隐藏实现细节的方式,因此类被实例化并被使用,就像在堆上分配单个实例一样。

Monostate 带来两大好处:

  • 一致语义。消费者像使用任何其他类一样使用单态。

  • 实现隐藏。 隐藏实现——类是静态的或单例的——如果以及何时,类实现需要更改,更改仅限于实现类的源文件。该类的消费者不知道这种变化。

    如果没有单态,当类实现发生变化时,类的每个使用者、对类或被更改成员的每个引用都必须同时更改,这可能会导致大量源文件分散在许多项目中。

这是一个将静态类作为 Monostate 的简单示例

public class MyMonostateClass
{
  #region internal, static implementation

  private static string dataItem;

  private static int someMethod( int foo )
  {
    return 0 ; // do something useful return the appropriate value
  }

  #endregion internal, static implementation

  #region public instance implementation wrappers

  public string DataItem
  {
    get { return dataItem; }
    set { dataItem = value; }
  }

  public int SomeMethod( int foo )
  {
    return MyMonostateClass.someMethod(foo);
  }

  #endregion public instance implementation wrappers

  public MyMonostateClass()
  {
    return ;
  }

}

和 Monostate 单例之一:

public class MyMonostateSingletonList : IList<int>
{
  private static readonly IList<int> instance = new List<int>() ;

  public MyMonostateSingletonList()
  {
    return ;
  }
  public int IndexOf( int item )
  {
    return instance.IndexOf(item) ;
  }
  public void Insert( int index , int item )
  {
    instance.Insert( index , item ) ;
  }
  public void RemoveAt( int index )
  {
    instance.RemoveAt( index ) ;
  }
  public int this[int index]
  {
    get
    {
      return instance[index] ;
    }
    set
    {
      instance[index] = value ;
    }
  }
  public void Add( int item )
  {
    instance.Add(item) ;
  }
  public void Clear()
  {
    instance.Clear() ;
  }
  public bool Contains( int item )
  {
    return instance.Contains(item) ;
  }
  public void CopyTo( int[] array , int arrayIndex )
  {
    instance.CopyTo( array , arrayIndex ) ;
  }
  public int Count
  {
    get { return instance.Count ; }
  }
  public bool IsReadOnly
  {
    get { return instance.IsReadOnly ; }
  }
  public bool Remove( int item )
  {
    return instance.Remove(item);
  }
  public IEnumerator<int> GetEnumerator()
  {
    return instance.GetEnumerator() ;
  }
  System.Collections.IEnumerator System.Collections.IEnumerable.GetEnumerator()
  {
    return instance.GetEnumerator() ;
  }
}

【讨论】:

  • 这种模式是不是违反了最小意外原则?例如:var list1 = new MyMonostateSingletonList(); list1.Add(1); var list2 = new MyMonostateSingletonList(); Console.Write(list2.Count); //Expect 0, but get 1
  • 你是什么意思?消费者得到一个黑盒类实例并要求它做事。黑盒子内发生的事情与类的消费者无关。
  • 对,但是如果我在我的范围内构造一个对象的私有实例,我希望我可以单独对其执行操作。在单例或静态类中,我不会有这样的期望。
【解决方案4】:

我会说没有优点或缺点,但这取决于您是否应该选择静态方法。考虑以下场景:

  • 您不应使用静态类/成员/方法:在一般情况下,例如 - 如果您需要存储和获取用户访问信息。
  • 您应该使用静态方法:如果您需要实现一些实用方法,例如发送电子邮件、记录错误、从 web.config 获取值、获取客户端的 IP 地址。

每当你写一个函数或声明一个变量时,它不会在内存中创建实例,直到你创建类的对象。但是,如果您使用 static 修饰符声明任何函数或变量,它会直接在内存中创建实例并全局执行。 static 修饰符不引用任何对象。

我希望这会有所帮助..

【讨论】:

    猜你喜欢
    • 1970-01-01
    • 2015-03-02
    • 1970-01-01
    • 2011-01-27
    • 1970-01-01
    • 1970-01-01
    • 1970-01-01
    • 1970-01-01
    相关资源
    最近更新 更多