我只是想了解静态方法的目的以及我可以创建静态方法的理想情况是什么,除了有些人会说静态方法用于创建助手。
考虑我有 1 个网站,将仅在我的公司中使用,例如像网站这样的人力资源管理系统。
现在,在管理员登录到系统后,管理员将看到 employees.so 列表,该方法很简单,它的作用只是从员工表中获取员工的所有详细信息,并将其显示在网站上,并且此方法将在业务访问层中定义,如下所示在.net中:
public class EmployeeBal
{
public List<Employee> GetAllEmployees()
{
return Select * from Employee
}
}
这就是我从应用程序中调用此方法的方式。例如(.aspx页面或 mvc 控制器等)
var employeeBal= new EmployeeBal();
employeeBal.GetAllEmployees();
所以我的问题是我应该将此方法创建为静态方法还是非静态方法?
注意:这只是方法的一个示例,此方法在我的业务访问层中。
考虑我有一个电子商务网站,在主页上我显示一些产品列表,并且在访问该网站时,每个用户都可以看到该产品列表。
所以我的函数将与上面在业务 acess 层中定义的相同:
public class ProductBal
{
public List<Product> DisplayProductonHomePage()
{
return Select * from Products
}
}
所以我的问题就像是将此方法创建为静态方法还是非静态方法一样,如果超过 10 个用户同时访问本网站会发生什么,那么这种方法的行为/含义是什么???
如果我们将此方法声明为静态,此方法将满足每个用户的目的吗?
任何人都可以通过简要解释每种情况来回答这个问题吗???
当没有要维护的状态时,静态方法很有意义。我所说的状态是什么意思?好吧,请考虑以下几点:您有两个不同的对象,a
和 b
,它们都是 EmployeeBal
类型。在您的程序中,是否有a.GetAllEmployees()
和b.GetAllEmployees()
会产生不同结果的情况?
如果不是,那么为什么对象a
和b
存在呢?拥有对象的全部意义在于将某种不同的状态与它们相关联。如果两个不同的对象永远不能指代不同的状态,那么它们就没有目的。
实际上,在这种情况下,您的EmployeeBal
将完全等同于System.Math
,并且它的所有方法都是"辅助方法"(如果您想这样称呼它们)。在这种情况下,暂时忘记静态方法:你的整个类应该是静态的(static class EmployeeBal
),并且它不应该有任何构造函数;因为类型 EmployeeBal
的对象的概念根本没有意义。事实上,在其他语言中,EmployeeBal
根本不是一个类;相反,它将是通常称为模块的东西:一个逻辑上对代码进行分组的单元。C# 没有模块,所有代码都必须驻留在类中。因此,类实现了双重目的:它们对代码进行分组,并生成对象。1
现在考虑一个不太极端的情况:EmployeeBal
对象实际上保持状态,并且不同。然而,无论哪个对象调用该方法,GetAllEmployees()
仍将产生相同的结果。
在这种情况下,EmployeeBal
显然不能是静态类。但GetAllEmployees
仍然是无状态的,因此不属于类型 EmployeeBal
的对象。因此,该方法应该是静态的。
1 两个根本不同的概念(模块和类)之间缺乏区别实际上很烦人,C# 以这种方式运行的主要原因是因为它被认为类似于 Java。事后看来,这是一个错误,但不是一个严重的错误。
该方法应该是静态的有什么原因吗?如果不是,我总是站在非静态一边。一个重要原因是能够编写单元测试。为了编写单元测试,您希望能够将要测试的类与其他类隔离开来。但是,如果类 A 包含对静态类 B 的引用,则无法在不测试 B 的情况下测试 A。也许 B 取决于连接字符串或配置设置。也许 B 依赖于其他静态类。现在你不能测试 A,除非 B 和它所依赖的一切都到位。
另一方面,如果类 A 依赖于通过其构造函数提供的接口(如 IEmployeeProvider
),那么您可以使用模拟的 IEmployeeProvider
实现来测试类 A。
如果 A 在其构造函数中IEmployeeProvider
作为参数,那么您可以通过查看构造函数来判断它依赖于IEmployeeProvider
。但是,如果它依赖于方法中某处的静态EmployeeProvider
类,则依赖项是隐藏的。你必须阅读整个班级才能知道它依赖于什么。
此外,静态类本身可能更难测试。除非它绝对总是保持无状态,否则最好有一个可以进行单元测试的非静态类。
可以让多个线程执行相同的静态方法,只要该方法不访问静态状态(如字段或属性)。在这种情况下,存储在字段/属性中的共享对象本身必须是线程安全的。.Net 的数据访问部分不是为线程安全的。
一旦开始考虑诸如管理可在执行单个 Web 请求期间重用于多个查询的数据库连接等方面,就应考虑静态是否是最佳方法。由于无法如上所述将连接存储在静态字段中,因此必须将其作为参数传递给每个静态方法。另一方面,如果将连接传递给构造函数并将其存储在(非静态)字段中,则可以从该实例的多个非静态方法访问它,这将使 IMO 更易于管理。
然而,这是一个相当大的话题,一般来说,类依赖关系的管理在 OOP 中要正确处理是相当棘手的。一些程序员更喜欢将此任务委托给"控制反转"库。有许多可用于.Net的,例如Microsoft Unity,StructureMap,AutoFac等。
回答您的问题:
所以我的问题是我应该将此方法创建为静态方法还是非静态方法?注意:这只是方法的一个示例,此方法在我的业务访问层中。
我会使这些方法静态 - 鉴于您提供的内容。但我敢打赌,你会在你的类中声明实例变量,或者在那个类的方法中声明实例变量,这当然意味着不要让它成为静态的。
因此,如果我决定是否使用静态方法,对我来说,一个决定性因素与重用和资源有关。
如果我发现自己多次重用一种方法,并且我得出结论它不需要状态(保存在内存中) - 我会让它成为一个静态方法。
此外,如果我可以在其他应用程序中使用,或者我认为它们将来会有用,我通常会使我的方法静态。
例如,我最近编写了一个将 excel 文件转换为平面文件的方法。我将其作为静态方法在其自己的静态类中(我可能会将其放在类似的实用程序类中),因为我最终可能会在另一个项目中再次使用它,因此我现在可以只引用它的类,而不必实例化一个新对象来调用该方法。(反正我不需要状态)
我对编程也很陌生,希望对您有所帮助。
如果我们要谈论静态,我们需要引入依赖关系。在本例中,它是一个 sql 客户端。下面是引入的代码的外观。由于我们不打算详细介绍 sql 客户端,因此它被用作静态方法中的接口。
var client = new SqlClient();
var allEmployeeData = EmployeeBal.GetAllEmployees(client);
class EmployeeBal
{
public static Employee GetAllEmployees(ISqlClient client)
{
return client.Execute("Select * from Employee");
}
}
通过接口注入依赖关系会改变一切。现在,该方法作为静态方法很好,因为它只处理一个接口和一个字符串。两者都是无国籍的。由于该方法的所有组件都是无状态的,因此对于只能具有一个全局状态的静态方法,它们是完全安全的。
由于您的代码最初是编写的,因此作为静态代码并不安全,因为我如何确保 sql 客户端已准备好使用,并且在我检查它已准备就绪后,当我去运行查询时它没有被更改?如果我可以注入 sql 客户端,我可以管理它,因为它具有本地与全局范围。
一个更好的例子是类似于 sql 客户端的工厂。例如,使用 nhibernate 时,应该只创建一个会话工厂。一个线程安全会话工厂可以创建多个非线程安全会话来运行 sql 查询。在这种情况下,通过静态方法公开会话工厂是合适的,因为这描述了只有一个会话工厂的事实。
var session = SessionFactory.OpenSession();
<</div>
div class="one_answers"> 使用静态方法等效于具有全局行为。它有好处:易于访问简单场景。
它还伴随着全球数据和状态的所有问题。其中,您不能用另一个实现替换一个实现(例如用于测试)。请参阅 https://softwareengineering.stackexchange.com/questions/148108/why-is-global-state-so-evil
虽然您可能会认为您没有全局状态...从概念上讲,你有。您有一种独特的、预先确定的、不可配置的、硬编码的方式来访问某些行为。您发布了它,但无法更改它...曾。你打破了开-关原则。你打破了利斯科夫替代原则。
Java有这个,但scala修改了这个。更多关于这一点:为什么 Scala 在类中没有静态成员?
静态和非静态方法的用例不同,因此您需要根据它们满足的需求创建一个:
- 静态方法
- 不参与基于继承的多态性,而非静态方法则参与。换句话说,不能将静态方法标记为虚拟或抽象,这意味着不能更改其行为。这也意味着静态方法的调用方确切地知道这个静态方法将要做什么以及如何执行。使用非静态方法,可以在基类上调用它,但由于多态性,您最终可能会调用具有重写行为的派生类方法。 静态
- 和非静态方法都可以改变某事的状态(而不是其他人声称的),但这是有区别的。您可以设计一个包含所有静态成员(属性、方法等)的静态类,因此这些方法可以更改此静态类的状态(也就是说,即使
C#
允许您这样做,我也不建议创建这样的类)。使用非静态方法,可以更改类的静态和非静态状态。这进一步探讨了静态类和非静态类之间的差异,简而言之:静态类是一个具体的实例,而非静态类可以相乘,每个类都有自己的状态副本(所以为什么要设计一个带有人为限制的静态类 - 这就是为什么我之前不推荐它们)。 - 静态方法的另一个很好的用法是扩展方法。它们应定义为静态,但您可以在它们要扩展的类的实例上调用它们。它们仍然充当实例的外部快捷方式,因为它们只能执行常规静态方法(例如无法访问私有或受保护成员)以外的任何操作。
- 你是对的,静态类非常适合定义帮助程序方法,因为这些方法通常只是一些固定功能的快捷方式,可以轻松地从许多地方重新执行它。在 Visual Basic 中,您可以使用
shared
关键字代替static
关键字,这很好地解释了静态方法的用途。
最后,我个人建议将静态方法创建为 Pure 函数,它们始终为相同的输入产生相同的输出(没有副作用,例如输出因时间或其他隐含因素而异)。你应该有一个强有力的理由来设计它(例如,如果你正在写Math.Random()
)。
现在,回答你问题中的观点(我知道,最后):
- 我认为业务访问层不应该是静态的,因为您很可能需要非静态类的好处,例如依赖注入和单元可测试性。
- 从线程/多线程的角度来看,静态方法和非静态方法之间没有区别,它们都可以由多个线程同时调用,并且所有线程将同时执行(除非使用同步构造)。但是,有一个常见的设计建议,即如果预期争用条件,则应使静态方法对线程安全。非静态方法不必担心这一点,因为这会使它们陷入太多的假设。