简体   繁体   English

如何在C#静态和非静态方法之间做出决定?

[英]How to decide between C# static and non-static methods?

[Edit] [编辑]

My original-question was "Why to decide between static and non-static? Both do the same..." 我的原始问题是“为什么要在静态和非静态之间做出决定?两者都是一样的......”

Unfortunately it was edited to a C#-specific question what I really wanted to avoid. 不幸的是,它被编辑成一个C#特定的问题,我真的想避免。

So, let me do some additions: 所以,让我做一些补充:

When I say interface, I don't mean the C#-keyword-interface but what I understand something like a C++-interface: A set of well defined functions to operate with my object. 当我说接口时,我不是指C#-keyword接口,而是我理解的东西,比如C ++接口:一组定义良好的函数来操作我的对象。 When saying weaken my interface, I mean I have different functions (static/non-static) that do the same thing. 当说削弱我的界面时,我的意思是我有不同的功能(静态/非静态)做同样的事情。 My interface is not well defined anymore when there are different functions to do the same thing. 当有不同的功能来做同样的事情时,我的界面不再被很好地定义。

So, as Bob the Janitor posted, I can implement a Validate()-function 所以,正如看门人Bob发布的那样,我可以实现一个Validate()函数

Document.Validate(myDocumentObject);    

but also 但是也

myConcreteDocumentObject.Validate();

To get back to my Copy()-example one could implement Copy() like 要返回我的Copy() - 示例可以实现Copy()之类的

myConcreteDocument.Copy(toPath);

but also 但是也

Document.Copy(myConcreteDocumentObject, toPath)

or 要么

Document.Copy(fromPath, toPath)

when I think of a folder that contains all the files belonging to my Document (in this case I'm not dependent of a concrete instance - but I'm dependent from other things :)). 当我想到一个文件夹,其中包含属于我的Document的所有文件(在这种情况下,我不依赖于具体的实例 - 但我依赖于其他东西:))。

In general I'm talking about static methods not static classes (sorry, if I forgot to mension). 一般来说,我说的是静态方法,而不是静态类(对不起,如果我忘了推荐)。

But as Anton Gogolev said I think my Document class is not a good example and not well designed so I think I will have to have a look at the Single Responsibility Principle. 但正如Anton Gogolev所说,我认为我的文档课不是一个很好的例子而且没有很好的设计,所以我想我必须看看单一责任原则。

I could also implement some kind of ManagerClass that operates with my DocumentClass: 我还可以实现某种与DocumentClass一起运行的ManagerClass:

For example: 例如:

myDocumentManagerObject.Copy(myConcreteDocumentObject, toPath);

or 要么

myDocumentManagerObject.Copy(myConcreteDocumentObject, toPath);

but if I refer to approach 1) I would tend to create objects that perform their tasks by themself rather than other objects (DocumentManager) that do something with my DocumentObject. 但是,如果我参考方法1)我倾向于创建自己执行任务的对象,而不是使用我的DocumentObject执行某些操作的其他对象(DocumentManager)。

(I hope this will not take the direction of a religious discussion about OOP ;).) (我希望这不会采取关于OOP的宗教讨论的方向;)。)

[/EDIT] [/编辑]


Old Version: 旧版本:

At first this seems to be a very basic question like "when to use static methods and when not" but this is something I'm confronted every now and then (and I have difficulties to describe what the real problem is; perhaps it's just to get reasons why (not) to use 1) or why (not) to use 2)). 起初这似乎是一个非常基本的问题,比如“何时使用静态方法,何时不使用”,但这是我偶尔遇到的问题(我很难描述真正的问题是什么;也许只是为了得到原因(不)使用1)或为什么(不)使用2))。

(Although I'm using C#-Syntax this is not a C#-restricted problem) (虽然我使用的是C#-Syntax,但这不是C#限制的问题)

In OOP there are two approaches (amongst others) of working with objects: 在OOP中,有两种处理对象的方法(以及其他方法):

1) If I want my object to do something, I just tell him to do so: 1)如果我想要我的对象做某事,我只是告诉他这样做:

myConcreteObject.DoSomething();

It's just like talking to an object. 这就像和一个对象交谈一样。

2) Or if you're a fan of static methods: 2)或者如果你是静态方法的粉丝:

ObjectClass.JustDoIt();

In some way I think static functions just "feel" better. 在某种程度上,我认为静态函数只是“感觉”更好。 So I tend to use static methods very often (to be independent from a concrete instance - independency is always good thing). 所以我倾向于经常使用静态方法(独立于具体实例 - 独立性总是好事)。

So, when designing a class I often have to decide if I take approach 1) or approach 2): 所以,在设计课程时,我经常要决定是采用方法1)还是方法2):

Imagine you have a class "Document" which should stand for a document that should be saved into a database: 想象一下,你有一个“文档”类,它应该代表一个应该保存到数据库中的文档:

A Document 一个文件

  • consists of one or more image files from filesystem (these become the single document pages) 由来自文件系统的一个或多个图像文件组成(这些文件成为单个文档页面)
  • has something like a bibliography - fields the user can add information about the document to - which is saved to an extra file 有类似参考书目的字段 - 用户可以添加有关文档的信息的字段 - 保存到额外的文件中
  • and should have some operations like Copy(), AddPage(), RemovePage() etc. 并且应该有一些操作,如Copy(),AddPage(),RemovePage()等。

Now I'm confrontated with several ways to create this class: 现在我遇到了几种创建这个类的方法:

//----- 1) non static approach/talking to objects -----
Document newDocument = new Document();

// Copy document to x (another database, for example)
newDocument.Copy(toPath);

I like this: I tell the document to copy itself to database x and the object does so by itself. 我喜欢这样:我告诉文档将自己复制到数据库x,对象本身就是这样做的。 Nice. 尼斯。

//----- 2) static approach ----------------------------
Document.Copy(myDocumentObject, toPath);

Why not? 为什么不? Also nice, feels very handy... 也不错,感觉非常方便......

So, which one to implement? 那么,实施哪一个? Both? 都? Or putting the static approach to a kind of helper class? 或者将静态方法放到一种辅助类中? Or choose approach 1) and stick with it to not weaken the interface of my Document-class? 或者选择方法1)并坚持使用它不会削弱我的Document类的界面?

When thinking about both approaches I come to the conclusion that (in theory) one could implement any function as a static function: 在考虑这两种方法时,我得出的结论是(在理论上)可以将任何函数实现为静态函数:

Class.Function(aConcreteClassObject, parameters);

but also non-static: 但也是非静态的:

aConcreteObject.DoSomething(parameters);

To give a real-world example: 举一个现实世界的例子:

[EDIT(Added parameter fromPath "Sorry, I forgot")] [编辑(从路径添加参数“对不起,我忘了”)]

//----- 2) static approach ----------------------------
File.Copy(fromPath, toPath);    // .Net-Framework-like

[/EDIT] [/编辑]

but also: 但是也:

//----- 1) non static approach ------------------------
ExampeFileClass fileObject = new ExampleFileClass();
fileObject.Copy(toPath);

or even (kind of OOP-Overkill): 甚至(OOP-Overkill的种类):

//----- 1) non static approach, too -------------------
fileObject.ToPath = @"C:\Test\file.txt";     // property of fileObject
fileObject.Copy();                           // copy to toPath

So, why (not) to use 1) or why (not) to use 2)? 那么,为什么(不)使用1)或为什么(不)使用2)?

(I would not concentrate on the Document class example too much, since it's more a general question about good class design.) (我不会过多地关注Document类示例,因为它更像是一个关于良好类设计的一般性问题。)

Here we go. 开始了。

First off: 首先:

So I tend to use static methods very often (to be independent from a concrete instance - independency is always good thing). 所以我倾向于经常使用静态方法(独立于具体实例 - 独立性总是好事)。

Quite the contrary: when using static methods you're very dependent on the concrete instance. 恰恰相反:使用静态方法时,您非常依赖于具体实例。

As far as your Document is concerned, I'd go neither way. 就你的Document而言,我不会这样做。 You've listed all responsibilities of Document class, which includes aggregation of data, saving itself to the database plus operations on pages and copying. 您已经列出了Document类的所有职责,其中包括数据聚合,将自身保存到数据库以及页面上的操作和复制。

This is way to much. 这是很多的方式。 Per SRP , each "module" (here "module" used as a catch-all term) should have only one reason to change. 根据SRP ,每个“模块”(此处“模块”用作全能术语)应该只有一个原因需要改变。 Your Document has lots of responsibilities, hence it has a whole slew of reasons to change. 您的Document有很多责任,因此它有很多改变的理由。 This is no good. 这不好。

With that in mind, I'd move all logic to other classes with strictly defined responsibilities. 考虑到这一点,我会将所有逻辑移到其他具有严格定义职责的类中。 A more or less accepted criterion of what to move was introduced, I believe, by either Herb Sutter or Andrei Alexandrescu, an is as follows: all operations (think methods) that can be performed with an object through its public contract should be moved outside the object in question. 我相信,Herb Sutter或Andrei Alexandrescu引入了或多或少可接受的移动标准,如下所示:所有可以通过公共合同与对象一起执行的操作(思考方法)应该移到外面有问题的对象。


You cannot use static methods to implement an interface, and you cannot override static methods. 您不能使用静态方法来实现接口,也不能覆盖静态方法。 So using static methods means that you are simply not doing OOP. 所以使用静态方法意味着你根本就不做OOP。

Think about how you would implement the following functionality using only static methods? 想想如何仅使用静态方法实现以下功能?

interface IDocument 
{
   void Print(IDevice targetDevice);
}

IDocument instance;

instance = new PdfDocument();
instance.Print(printer);

instance = new WordDocument();
instance.Print(printer);

KISS. 吻。 If you don't have to call a constructor, even better. 如果你不必调用构造函数,那就更好了。

Also, a method being static should tell you a little about how the function operates: 另外,一个静态方法应该告诉你一些函数如何运作:

  • It doesn't operate on variables outside of what's passed to it. 它不会对传递给它的变量进行操作。
  • It doesn't require any memory other than when the method is called (not counting what is returned from the function) 除了调用方法之外,它不需要任何内存(不计算从函数返回的内容)

There are some other important things to note: 还有一些需要注意的重要事项:

  • Static methods in some instances (Java) are not able to be overridden/subclassed, so they are better suited for cases where the implementation will not need to change . 某些实例中的静态方法(Java)无法被覆盖/子类化,因此它们更适合于不需要更改实现的情况。
  • Some would argue that static methods are intrinsically difficult to test . 有些人认为静态方法本质上难以测试

I would also refer to this thread , and a simple google search which frankly provides copious amounts of discussion on this very topic. 我也会参考这个帖子 ,以及一个简单的谷歌搜索 ,坦率地提供大量关于这个话题的讨论。

My "rule" is: 我的“规则”是:

  • If I don't need to use properties from my class, make it static. 如果我不需要使用我的类中的属性,请将其设置为静态。 (in other words, if the method is not really attached to the class, just there for logic association, use static ) (换句话说,如果方法没有真正附加到类,只是用于逻辑关联,使用静态)

In general if you have method like: 一般来说,如果你有这样的方法:

Document.Copy(myDocumentObject, toPath);

I think it is better to use a non-static method, because the first parameter being a Document suggests that it is really an operation on the document. 我认为最好使用非静态方法,因为第一个参数是Document,表明它实际上是对文档的操作。

In general, when programming using an OO mindset, you're going to want to avoid using static methods. 通常,在使用OO思维模式编程时,您将要避免使用静态方法。 In OOP, the idea is to represent everything as objects, and to give each object a clear set of abilities that represents its core abstraction. 在OOP中,我们的想法是将所有东西都表示为对象,并为每个对象提供一组清晰的能力来代表其核心抽象。 Static methods "break" this abstraction. 静态方法“打破”这种抽象。

Your example talking about a Document class with a copy method is a prime example. 您使用复制方法讨论Document类的示例就是一个很好的例子。 I would argue that the correct OO implementation is the first way . 我认为正确的OO实现是第一种方式 That is, to have copy as an instance method like this: 也就是说,要将副本作为这样的实例方法:

document1.copy(toPath)

It makes sense that the ability to copy itself is part of a Documents core abstraction. 复制自身的能力是文档核心抽象的一部分是有道理的。 In this way, the client code sending the copy message only has to specify where to copy to , because it is understood that a Document keeps track of where it is located internally. 通过这种方式,发送复制消息的客户端代码只需指定要复制到的位置 ,因为可以理解文档会跟踪内部的位置。 There is no need for that information to be replicated anywhere else, which is a major problem with the third option you present that looks like this: 没有必要在其他任何地方复制该信息,这是您提供的第三个选项的主要问题,如下所示:

Document.copy(fromPath, toPath)

If you have to ask, don't use statics. 如果你不得不问,不要使用静力学。

Actual rule of thumb (and there are plenty of real technical reasons but I find this helps explain the concepts): 实际的经验法则(有很多真正的技术原因,但我发现这有助于解释这些概念):

  • If the class in question can exist multiple times it's not static. 如果有问题的类可以多次存在,那么它不是静态的。

  • If the method in question acts against the instance information it's not static. 如果所讨论的方法对实例信息起作用,则它不是静态的。

  • If the method or class is about meta-information it's static. 如果方法或类是关于元信息的,那么它是静态的。

With those guidelines it's clear that files and documents are multiples and copying is an act against the instance. 根据这些指导原则,文件和文档很明显是倍数,复制是针对实例的行为。 The method should not be static. 该方法不应该是静态的。

Static Methods are can be very useful, I love extension methods, but they force coupling and if used improperly can make testing a nightmare! 静态方法可能非常有用,我喜欢扩展方法,但它们强制耦合,如果使用不当可能会使测试成为一场噩梦!

A good example of when to use a static is when you want to do so validation 何时使用静态的一个很好的例子是你想要进行验证

public static errors Validate(Document myDoc)
{
..some validation code
}

this is very testable and it doesn't mater that your tightly coupling the method to an object. 这是非常可测试的,并且它不会使您将方法与对象紧密耦合。 A Bad place to use a static method is when it dose something other then just return something, an example would be in a Biz layer that validates an object and if it passes validation it save the data to the DB 使用静态方法的不好的地方是当它给其他东西加剂然后只返回一些东西时,一个例子是在验证对象的Biz层中,如果它通过验证它将数据保存到DB

public static errors ValidateAndSave(Document myDoc)
{
    errors docErrors = Validate(myDoc);
    if(docErrors.count==0)
    {
         docErrors = SaveToDB(myDoc);
    }

   return docErrors; 
} 

This is a real pain to test because every time you run it, and it passes validation your taking to the database, your Biz logic may not generate an error but but your DAL layer might, so instead of only testing the functionality of the Biz layer your also having to test the DAL layer as well, and your tightly coupling your object, your Biz layer and your Dal together making this very hard to test and maintain. 这是一个真正的测试难题,因为每次运行它,并且它通过验证您的数据库,您的Biz逻辑可能不会生成错误,但您的DAL层可能,所以而不是只测试Biz层的功能您还必须测试DAL层,并将您的对象,Biz层和Dal紧密耦合在一起,这使得测试和维护非常困难。

In general, I would say that "copying" oneself, as far as an object is concerned, usually means cloning one's data into a new object. 一般来说,我会说,就对象而言,“复制”自己通常意味着将一个人的数据克隆到一个新对象中。 The "copying" described here is something the filesystem is doing on your behalf, not the object. 这里描述的“复制”是文件系统代表你做的事情,而不是对象。 As such, I'd make it a static method and not a method on a Document instance. 因此,我将它作为静态方法而不是Document实例上的方法。

Same as altCongnito, and i will add that fileObject.Copy everybody will use, more than the object fileObject. 与altCongnito相同,我将添加每个人将使用的fileObject.Copy,而不是对象fileObject。 Static for a function that have ideal relationship with the class and not functional dependency of it. 静态的函数与类具有理想的关系而不是函数的依赖性。

If you use any other objects then you shokld default to instance level methods so that you can configure those dependencies using Dependancy Injection. 如果您使用任何其他对象,则将shokld默认为实例级方法,以便您可以使用Dependancy Injection配置这些依赖项。

For example, if one of those images was an SVG image then you may have a dependency on an XML parser which (in Java at least) have many implementations, likewise for SVG renderers I imagine and many other constituent image types may require similar arrangements that evolve as the state of the object evolves or which must be changed in different usage scenarios (eg test, production, different projects re-using your code). 例如,如果其中一个图像是SVG图像,那么您可能依赖于XML解析器(至少在Java中)具有许多实现,同样对于我想象的SVG渲染器,许多其他组成图像类型可能需要类似的安排,随着对象的状态演变或必须在不同的使用场景中进行更改(例如测试,生产,重新使用代码的不同项目)。

The blinking amber warning light is that you may be using classes that are not part of your framework's default libraries, so you've made a choice of a third party component and if using statics you are not well placed to modify that decision. 闪烁的琥珀色警告灯表示您可能正在使用不属于框架默认库的类,因此您可以选择第三方组件,如果使用静态,则无法修改该决策。

A useful "redline" is that if you touch another process (database server, web service etc) then I'd regard a static method as bad 100% of the time as this makes unit testing more dificult. 一个有用的“红线”是,如果你触摸另一个进程(数据库服务器,Web服务等),那么我会认为静态方法在100%的时间都不好,因为这会使单元测试更加困难。

声明:本站的技术帖子网页,遵循CC BY-SA 4.0协议,如果您需要转载,请注明本站网址或者原文地址。任何问题请咨询:yoyou2525@163.com.

相关问题 C#中的Helper方法:静态还是非静态? - Helper methods in C#: Static or non-static? 在C#中编写静态和非静态方法时,如何避免出现“调用不明确……”错误? - How do I avoid 'call is ambiguous…' error when writing static and non-static methods in C#? 在 C# 中调用具有方法属性的非静态方法 - Call non-static methods with method attributes in C# 任务中用作Func参数的静态或非静态方法 <TResult> C#中的构造函数? - Static or non-static methods used as the Func parameter in a Task<TResult> constructor in C#? 从非静态 class Z240AA2CEC4B29C56F3BEE520ADCEE7E 中的 static 方法中的对象释放 memory 块 - Releasing memory blocks from objects in static methods inside a non-static class c# 如何从C#中的线程访问非静态方法 - How to access a non-static method from a thread in C# 为什么 C# 允许在接口内部定义非抽象、非静态方法? - Why does C# allows definitions of non-abstract, non-static methods inside of an interface? 非静态事物是否来自静态C#? - Do Non-Static things from Static, C#? 静态类 JINT 中的 C# 非静态类 - C# non-static class inside static class JINT C#中相同功能的静态和非静态版本 - Static and non-static version of the same function in C#
 
粤ICP备18138465号  © 2020-2024 STACKOOM.COM