简体   繁体   English

为什么 C# 不允许像 C++ 这样的非成员函数

[英]Why C# is not allowing non-member functions like C++

C# will not allow to write non-member functions and every method should be part of a class. C# 不允许编写非成员函数,每个方法都应该是类的一部分。 I was thinking this as a restriction in all CLI languages.我认为这是所有 CLI 语言中的限制。 But I was wrong and I found that C++/CLI supports non-member functions.但是我错了,我发现 C++/CLI 支持非成员函数。 When it is compiled, compiler will make the method as member of some unnamed class.编译时,编译器会将方法作为某个未命名类的成员。

Here is what C++/CLI standard says,这是 C++/CLI 标准所说的,

[Note: Non-member functions are treated by the CLI as members of some unnamed class; [注意:CLI 将非成员函数视为某个未命名类的成员; however, in C++/CLI source code, such functions cannot be qualified explicitly with that class name.但是,在 C++/CLI 源代码中,不能使用该类名显式限定此类函数。 end note]尾注]

The encoding of non-member functions in metadata is unspecified.元数据中非成员函数的编码未指定。 [Note: This does not cause interop problems because such functions cannot have public visibility. [注意:这不会导致互操作问题,因为此类功能无法公开。 end note]尾注]

So my question is why don't C# implement something like this?所以我的问题是为什么 C# 不实现这样的东西? Or do you think there should not be non-member functions and every method should belong to some class?或者你认为不应该有非成员函数,每个方法都应该属于某个类?

My opinion is to have non-member function support and it helps to avoid polluting class's interface.我的观点是有非成员函数支持,它有助于避免污染类的接口。

Any thoughts..?有什么想法吗..?

See this blog posting:看到这个博客帖子:

http://blogs.msdn.com/ericlippert/archive/2009/06/22/why-doesn-tc-implement-top-level-methods.aspx http://blogs.msdn.com/ericlippert/archive/2009/06/22/why-doesn-tc-implement-top-level-methods.aspx

(...) (……)

I am asked "why doesn't C# implement feature X?"我被问到“为什么 C# 不实现功能 X?” all the time.每时每刻。 The answer is always the same: because no one ever designed, specified, implemented, tested, documented and shipped that feature.答案总是一样的:因为没有人设计、指定、实施、测试、记录和发布该功能。 All six of those things are necessary to make a feature happen.所有这六件事都是使功能发生所必需的。 All of them cost huge amounts of time, effort and money.所有这些都花费了大量的时间、精力和金钱。 Features are not cheap, and we try very hard to make sure that we are only shipping those features which give the best possible benefits to our users given our constrained time, effort and money budgets.功能并不便宜,而且我们非常努力地确保我们只发布那些在时间、精力和金钱预算有限的情况下为我们的用户带来最大利益的功能。

I understand that such a general answer probably does not address the specific question.我知道这样一个笼统的答案可能没有解决具体问题。

In this particular case, the clear user benefit was in the past not large enough to justify the complications to the language which would ensue.在这种特殊情况下,过去明显的用户利益不足以证明随之而来的语言的复杂性是合理的。 By stricting how different language entities nest inside each other we (1) restrict legal programs to be in a common, easily understood style, and (2) make it possible to define "identifier lookup" rules which are comprehensible, specifiable, implementable, testable and documentable.通过限制不同语言实体如何相互嵌套,我们 (1) 将合法程序限制为通用、易于理解的风格,以及 (2) 可以定义可理解、可指定、可实现、可测试的“标识符查找”规则和可记录的。

By restricting method bodies to always be inside a struct or class, we make it easier to reason about the meaning of an unqualified identifier used in an invocation context;通过限制方法体总是在结构体或类中,我们可以更容易地推断调用上下文中使用的非限定标识符的含义; such a thing is always an invocable member of the current type (or a base type).这样的东西始终是当前类型(或基类型)的可调用成员。

(...) (……)

and this follow-up posting:和这个后续帖子:

http://blogs.msdn.com/ericlippert/archive/2009/06/24/it-already-is-a-scripting-language.aspx http://blogs.msdn.com/ericlippert/archive/2009/06/24/it-already-is-a-scripting-language.aspx

(...) (……)

Like all design decisions, when we're faced with a number of competing, compelling, valuable and noncompossible ideas, we've got to find a workable compromise.像所有设计决策一样,当我们面临许多相互竞争的、引人注目的、有价值的和不可组合的想法时,我们必须找到可行的折衷方案。 We don't do that except by considering all the possibilites , which is what we're doing in this case.我们不这样做,除非考虑所有的可能性,这就是我们在这种情况下所做的。

(emphasis from original text) (重点来自原文)

C# doesn't allow it because Java didn't allow it. C# 不允许,因为 Java 不允许。

I can think of several reasons why the designers of Java probably didn't allow it我可以想到 Java 的设计者可能不允许它的几个原因

  • Java was designed to be simple. Java 被设计成简单的。 They attempted to make a language without random shortcuts, so that you generally have just one simple way to do everything, even if other approaches would have been cleaner or more concise.他们试图创造一种没有随机快捷方式的语言,因此您通常只有一种简单的方法来完成所有事情,即使其他方法会更清晰或更简洁。 They wanted to minimize the learning curve, and learning "a class may contain methods" is simpler than "a class may contain methods, and functions may exist outside classes".他们想最小化学习曲线,学习“一个类可能包含方法”比“一个类可能包含方法,并且函数可能存在于类之外”更简单。
  • Superficially, it looks less object-oriented.从表面上看,它看起来不太面向对象。 (Anything that isn't part of an object obviously can't be object-oriented? Can it? of course, C++ says yes, but C++ wasn't involved in this decision) (任何不属于对象的东西显然不能是面向对象的?可以吗?当然,C++ 说是的,但 C++ 没有参与这个决定)

As I already said in comments, I think this is a good question, and there are plenty of cases where non-member functions would've been preferable.正如我在评论中已经说过的,我认为这是一个很好的问题,在很多情况下,非成员函数会更可取。 (this part is mostly a response to all the other answers saying "you don't need it") (这部分主要是对所有其他回答说“你不需要它”的回应)

In C++, where non-member functions are allowed, they are often preferred, for several reasons:在 C++ 中,允许使用非成员函数,它们通常是首选,原因如下:

  • It aids encapsulation.它有助于封装。 The fewer methods have access to the private members of a class, the easier that class will be to refactor or maintain.可以访问类的私有成员的方法越少,该类就越容易重构或维护。 Encapsulation is an important part of OOP.封装是OOP的重要组成部分。
  • Code can be reused much easier when it is not part of a class.当代码不是类的一部分时,它可以更容易地重用。 For example, the C++ standard library defines std::find or std::sort` as non-member functions, so that they can be reused on any type of sequences, whether it is arrays, sets, linked lists or (for std::find, at least) streams.例如,C++ 标准库将std::find或 std::sort` 定义为非成员函数,以便它们可以在任何类型的序列上重用,无论是数组、集合、链表或(对于 std: :找到,至少)流。 Code reuse is also an important part of OOP.代码重用也是 OOP 的重要组成部分。
  • It gives us better decoupling.它为我们提供了更好的解耦。 The find function doesn't need to know about the LinkedList class in order to be able to work on it. find函数不需要知道 LinkedList 类就可以处理它。 If it had been defined as a member function, it would be a member of the LinkedList class, basically merging the two concepts into one big blob.如果它被定义为一个成员函数,它将是 LinkedList 类的一个成员,基本上将两个概念合并为一个大 blob。
  • Extensibility.可扩展性。 If you accept that the interface of a class is not just "all its public members", but also "all non-member functions that operate on the class", then it becomes possible to extend the interface of a class without having to edit or even recompile the class itself.如果您接受类的接口不仅是“其所有公共成员”,而且是“对类进行操作的所有非成员函数”,那么就可以扩展类的接口而无需编辑或甚至重新编译类本身。

The ability to have non-member functions may have originated with C (where you had no other choice), but in modern C++, it is a vital feature in its own right, not just for backward-comparibility purposes, but because of the simpler, cleaner and more reusable code it allows.拥有非成员函数的能力可能起源于 C(在那里你别无选择),但在现代 C++ 中,它本身就是一个重要的特性,不仅仅是为了向后比较的目的,而且因为更简单,它允许更干净和更可重用的代码。

In fact, C# seems to have realized much the same things, much later.事实上,C# 似乎已经实现了很多相同的事情,但要晚得多。 Why do you think extension methods were added?为什么你认为添加了扩展方法? They are an attempt at achieving the above, while preserving the simple Java-like syntax.它们是实现上述目标的一种尝试,同时保留了类似 Java 的简单语法。 Lambdas are also interesting examples, as they too are essentially small functions defined freely, not as members of any particular class. Lambda 也是有趣的例子,因为它们本质上也是自由定义的小函数,而不是任何特定类的成员。 So yes, the concept of non-member functions is useful, and C#'s designers have realized the same thing.所以是的,非成员函数的概念很有用,C# 的设计者也意识到了同样的事情。 They've just tried to sneak the concept in through the back door.他们只是试图从后门偷偷摸摸这个概念。

http://www.ddj.com/cpp/184401197 and http://www.gotw.ca/publications/mill02.htm are two articles written by C++ experts on the subject. http://www.ddj.com/cpp/184401197http://www.gotw.ca/publications/mill02.htm是 C++ 专家针对该主题撰写的两篇文章。

Non member functions are a good thing because they improve encapsulation and reduce coupling between types.非成员函数是一件好事,因为它们改进了封装并减少了类型之间的耦合。 Most modern programming languages such as Haskell and F# support free functions.大多数现代编程语言(例如 Haskell 和 F#)都支持免费函数。

What's the benefit of not putting each method in a named class?将每个方法放在命名类中有什么好处? Why would a non-member function "pollute" the class's interface?为什么非成员函数会“污染”类的接口? If you don't want it as part of the public API of a class, either don't make it public or don't put it in that class.如果您不想将其作为类的公共 API 的一部分,请不要将其公开或不要将其放在该类中。 You can always create a different class.您始终可以创建不同的类。

I can't remember ever wanting to write a method floating around with no appropriate scope - other than anonymous functions, of course (which aren't really the same).我不记得曾经想要编写一个没有适当范围的方法——当然,除了匿名函数(它们实际上并不相同)。

In short, I can't see any benefit in non-member functions, but I can see benefits in terms of consistency, naming and documentation in putting all methods in an appropriately named class.简而言之,我看不到非成员函数有任何好处,但我可以看到将所有方法放在一个适当命名的类中的一致性、命名和文档方面的好处。

The CLS (common language specification) says that you shouldn't have non-member functions in a library that conforms to the CLS. CLS(通用语言规范)说在符合 CLS 的库中不应该有非成员函数。 It's like an extra set of restrictions in addition to the basic restrictions of the CLI (common language interface).它就像是在 CLI(通用语言接口)的基本限制之外的额外限制。

It is possible that a future version of C# will add the ability to write a using directive that allows the static members of a class to be accessed without the class name qualification: C# 的未来版本可能会添加编写using指令的功能,该指令允许在没有类名限定的情况下访问类的静态成员:

using System.Linq.Enumerable; // Enumerable is a static class

...

IEnumerable<int> range = Range(1, 10); // finds Enumerable.Range

Then there will be no need to change the CLS and existing libraries.这样就无需更改 CLS 和现有库。

These blog posts demonstrate a library for functional programming in C#, and they use a class name that is just one letter long, to try and cut down the noise caused by the requirement to qualify static method calls. 这些博客文章演示了一个用于 C# 函数式编程的库,它们使用一个只有一个字母长的类名,以尝试减少由限定静态方法调用的要求引起的噪音。 Examples like that would be made a little nicer if using directives could target classes.如果using指令可以针对类,这样的示例会更好一些。

  • Having all code lie within classes allows for a more powerful set of reflection capabilities.将所有代码放在类中可以提供更强大的反射功能。
  • It allows the use of static intializers, which can initialize the data needed by static methods within a class.它允许使用静态初始化器,它可以初始化类中静态方法所需的数据。
  • It avoids name clashes between methods by explicitly enclosing them within a unit that cannot be added to by another compilation unit.它通过将方法显式地包含在另一个编译单元无法添加的单元中来避免方法之间的名称冲突。

Since Java, most programmers have easily accepted that any method is a member of a class.从 Java 开始,大多数程序员很容易接受任何方法都是类的成员。 I doesn't make any considerable obstacles and make the concept of method more narrow, which make a language easier.我没有设置任何相当大的障碍并使方法的概念更加狭窄,这使语言更容易。

However, indeed, class infers object, and object infers state, so the concept of class containing only static methods looks a little absurd.但是,确实,类推断对象,对象推断状态,因此仅包含静态方法的类的概念看起来有点荒谬。

I think you really need to clarify what you would want to create non-member static methods to achieve.我认为你真的需要澄清你想要创建非成员静态方法来实现什么。

For instance, some of the things you might want them for could be handled with Extension Methods例如,您可能需要它们的某些事情可以使用扩展方法处理

Another typical use (of a class which only contains static methods) is in a library.另一个典型用途(仅包含静态方法的类)是在库中。 In this case, there is little harm in creating a class in an assembly which is entirely composed of static methods.在这种情况下,在完全由静态方法组成的程序集中创建类几乎没有什么害处。 It keeps them together, avoids naming collisions.它将它们保持在一起,避免命名冲突。 After all, there are static methods in Math which serve the same purpose.毕竟,Math 中有一些静态方法可以达到同样的目的。

Also, you should not necessarily compare C++'s object model with C#.此外,您不必将 C++ 的对象模型与 C# 进行比较。 C++ is largely (but not perfectly) compatible with C, which didn't have a class system at all - so C++ had to support this programming idiom out of the C legacy, not for any particular design imperative. C++ 在很大程度上(但不完全)与 C 兼容,C 根本没有类系统——所以 C++ 必须支持 C 遗留之外的这种编程习惯,而不是为了任何特定的设计要求。

Bear something in mind: C++ is a much more complicated language than C#.请记住:C++ 是一种比 C# 复杂得多的语言。 And although they may be similiar syntactically, they are very different beasts semantically.尽管它们在句法上可能相似,但它们在语义上却是截然不同的野兽。 You wouldn't think it would be terribly difficult to make a change like this, but I could see how it could be.你不会认为做出这样的改变会非常困难,但我可以看到它是如何发生的。 ANTLR has a good wiki page called What makes a language problem hard? ANTLR 有一个很好的 wiki 页面,称为什么使语言问题变得困难? that's good to consult for questions like this.像这样的问题咨询是很好的。 In this case:在这种情况下:

Context sensitive lexer?上下文敏感词法分析器? You can't decide what vocabulay symbol to match unless you know what kind of sentence you are parsing.除非您知道要解析的句子类型,否则您无法确定要匹配的词汇符号。

Now instead of just worrying about functions defined in classes, we have to worry about functions defined outside classes.现在,我们不仅要担心在类中定义的函数,还要担心在类外定义的函数。 Conceptually, there isn't much difference.从概念上讲,没有太大区别。 But in terms of lexing and parsing the code, now you have the added problem of having to say "if a function is outside a class, it belongs to this unnamed class. However, if it is inside the class, then it belongs to that class."但是在词法分析和代码解析方面,现在你不得不说“如果一个函数在一个类之外,它属于这个未命名的类。但是,如果它在类内部,那么它属于那个类”班级。”

Also, if the compiler comes across a method like this:另外,如果编译器遇到这样的方法:

public void Foo()
{
    Bar();
}

...it now has to answer the question "is Bar located within this class or is it a global class?" ...它现在必须回答“Bar 是位于这个类中还是它是一个全局类?”的问题。

Forward or external references?转发或外部引用? Ie, multiple passes needed?即,需要多次通过? Pascal has a "forward" reference to handle intra-file procedure references, but references to procedures in other files via the USES clauses etc... require special handling. Pascal 有一个“转发”引用来处理文件内过程引用,但是通过 USES 子句等引用其他文件中的过程需要特殊处理。

This is another thing that causes problems.这是导致问题的另一件事。 Remember that C# doesn't require forward declarations.请记住,C# 不需要前向声明。 The compiler will make one pass just to determine what classes are named and what functions those classes contain.编译器将通过一次来确定命名了哪些类以及这些类包含哪些函数。 Now you have to worry about finding classes and functions where functions can be either inside or outside of a class.现在您必须担心查找函数可以位于类内部或外部的类函数。 This is something a C++ parser doesn't have to worry about as it parses everything in order.这是 C++ 解析器不必担心的事情,因为它会按顺序解析所有内容。

Now don't get me wrong, it could probably be done in C#, and I would probably use such a feature.现在不要误会我的意思,它可能可以在 C# 中完成,我可能会使用这样的功能。 But is it really worth all the trouble of overcoming these obstacles when you could just type a class name in front of a static method?但是,当您只需在静态方法前键入类名时,克服这些障碍是否真的值得?

While it's true you need a class (eg a static class called FreeFunctions ) to hold such functions, you're free to place using static FreeFunctions;虽然确实需要一个类(例如称为FreeFunctions的静态类)来保存这些函数,但您可以using static FreeFunctions;自由放置using static FreeFunctions; at the top of any file that needs the functions from it, without having to litter your code with FreeFunctions.在需要函数的任何文件的顶部,而不必用FreeFunctions.乱扔代码FreeFunctions. qualifiers.预选赛。 I'm not sure if there's actually a case where this is demonstrably inferior to not requiring the function definitions to be contained in a class.我不确定是否真的存在这样的情况,这明显不如不要求将函数定义包含在类中。

Look, other programming languages have a hard time to define the internal nature of a function instance from the compiler's point of view.看,其他编程语言很难从编译器的角度定义函数实例的内部性质。 In Pascal and C, the instances are basically defined as something that can be processed as pointer only .在 Pascal 和 C 中,实例基本上被定义为只能作为指针处理的东西。 Especially, since reading/writing to executable code positions is what 7 out of 9 computer science professors are dead set against.特别是,由于读/写可执行代码位置是 9 位计算机科学教授中有 7 位死心塌地反对的。 As member of a class, no one does need to care how to treat its manifestation because this manifestation's type is derived from a class property.作为一个类的成员,没有人需要关心如何对待它的表现,因为这个表现的类型是从一个类属性派生的。 It is possible to create something that is exactly processed like a global function: a lambda function, assigned to a variable:可以创建一些完全像全局函数一样处理的东西:一个 lambda 函数,分配给一个变量:

 Func<int,int> myFunc = delegate(int var1) { Console.WriteLine("{0}",var1*2); return var1*3; };
. . And it can simply be called like a global function by its variable name. 它可以像全局函数一样简单地通过变量名调用。 If so, the difference would be implementing a new object type on the lowest level with same behavior as another one. 如果是这样,区别在于在最低级别实现一个新的对象类型,其行为与另一个对象类型相同。 That is considered bad practice by experienced programmers, and was perhaps scrapped because of this. 这被有经验的程序员认为是不好的做法,可能因此而被废弃。

Free functions are very useful if you combine them with duck typing.如果将自由函数与鸭子类型结合使用,它们将非常有用。 The whole C++ STL is based on it.整个 C++ STL 都基于它。 Hence I am sure that C# will introduce free functions when they manage to add true generics.因此,我确信 C# 在设法添加真正的泛型时会引入自由函数。

Like economics, language design is also about psychology.与经济学一样,语言设计也与心理学有关。 If you create appetite for true generics via free functions in C# and not deliver, then you would kill C#.如果您通过 C# 中的免费函数对真正的泛型产生兴趣而不交付,那么您将扼杀 C#。 Then all C# developers would move to C++ and nobody wants that to happen, not the C# community and most certainly not those invested in C++.然后所有 C# 开发人员都将转向 C++,没有人希望这种情况发生,C# 社区和投资 C++ 的人当然也不会。

Csharp does not have non-member function because it has copied or inspired by java's philosophy that only OOPs is the solution for all the problems and it will only allow things to be solved using OO way. Csharp 没有非成员函数,因为它复制或受 Java 哲学的启发,即只有 OOP 是所有问题的解决方案,它只允许使用 OO 方式解决问题。

Non-member functions are very important feature if we really want to do generic programming.如果我们真的想做泛型编程,非成员函数是非常重要的特性。 They are more reusable compared to putting them in a class.与将它们放在一个类中相比,它们更具可重用性。

CSharp has to come up with ExtensionMethods due to absence of non-member functions.由于缺少非成员函数,CSharp 不得不提出 ExtensionMethods。

As now programming languages are moving towards functional programming paradigm and it seems to be the better way to approach and solve the problem and is the future.现在编程语言正朝着函数式编程范式发展,它似乎是解决问题的更好方法,也是未来。 CSharp should rethink about it. CSharp 应该重新考虑一下。

If you think about it, the counter argument would be "why doesn't C++ support extension methods" and the answer lies within their design goals. 如果您考虑一下,计数器参数将是“为什么C ++不支持扩展方法” ,答案就在其设计目标之内。

C++ gives you namespace functions that you can call on any object which would help you "extending" that object in some way. C ++为您提供了可以在任何对象上调用的名称空间函数,这些函数将以某种方式帮助您“扩展”该对象。

Most of the times I used namespace functions in C++ were to create functions that accept objects and do functionality that I don't want to put inside a class member function. 我在C ++中使用命名空间函数的大多数时间是创建可以接受对象的函数,以及执行我不想放入类成员函数中的函数。

In C# you can create an extension method that does the job for you (most of the time). 在C#中,您可以创建一个扩展方法来为您完成工作(大部分时间)。 For what's left of the cases you will miss that feature. 对于剩下的情况,您将错过该功能。

Take the following code for example: 以以下代码为例:

template<typename T>
unsigned int findCount(vector<T>& vec, T data){
    unsigned int count = 0;
    for(auto val : vec)
        if(val == data) ++count;
    return count;
}

Now if I needed that functionality for some purpose in my class, I can just add it to the class's namespace and use it without "polluting" my class with that function. 现在,如果我出于某种目的需要在类中使用该功能,则可以将其添加到类的名称空间中并使用它,而无需使用该函数“污染”我的类。

In C# you can achieve the same goal with extension method: 在C#中,您可以使用扩展方法实现相同的目标:

static class Extensions {
    public static uint FindCount<T>(this List<T> list, T data) {
        uint counts = 0;
        foreach (var item in list)
            if (item.Equals(data)) ++counts;
        return counts;
    }
}

And both of them work the same way which is cool. 他们两个都以相同的方式工作,这很酷。 Many will argue about C++ missing extension methods, many will argue about C# missing namespace functions. 许多人会争论C ++缺少扩展方法,许多人会争论C#缺少名称空间功能。

I think the best way to put it is by not comparing these languages to some detail because they are different languages with different implementations. 我认为最好的表达方式是不对这些语言进行比较,因为它们是具有不同实现的不同语言。

As you quoted in your question, C++/CLI does not "really" support namespace member functions since it adds an unnamed class to use them which is by no way close to C++ implementation, maybe close in the way it looks, but really different. 正如您在问题中所引用的那样,C ++ / CLI并不“真正”支持名称空间成员函数,因为它添加了一个未命名的类来使用它们,这与C ++的实现绝不相近,也许看起来相近,但确实有所不同。

In the end, functionality is always something to look forward to, and as much as I want namespace member functions in C# is as much as I want extension methods in C++. 最后,功能总是值得期待的,而我希望C#中的名称空间成员函数和C ++中的扩展方法一样多。 Which isn't much anyway. 无论如何,这并不多。

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

 
粤ICP备18138465号  © 2020-2024 STACKOOM.COM