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

Nav*_*K N 65 .net c# clr c++-cli function

C#将不允许编写非成员函数,并且每个方法都应该是类的一部分.我认为这是所有CLI语言的限制.但我错了,我发现C++/CLI支持非成员函数.编译时,编译器会将该方法作为某个未命名类的成员.

这是C++/CLI标准所说的,

[注意:CLI将非成员函数视为某些未命名类的成员; 但是,在C++/CLI源代码中,此类函数无法使用该类名显式限定.结束说明]

未指定元数据中非成员函数的编码.[注意:这不会导致互操作问题,因为此类功能无法获得公共可见性.结束说明]

所以我的问题是为什么C#不实现这样的东西?或者你认为不应该有非成员函数,每个方法应该属于某个类?

我的意见是拥有非成员函数支持,它有助于避免污染类的接口.

有什么想法吗..?

Eri*_*ert 86

看到这篇博文:

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

(......)

我被问到"为什么C#不实现功能X?" 每时每刻.答案总是一样的:因为没有人设计,指定,实施,测试,记录和发送该功能.所有这六件事都是实现这一功能所必需的.所有这些都耗费了大量的时间,精力和金钱.功能并不便宜,我们非常努力地确保我们只提供那些能够为我们的用户提供最佳利益的功能,因为我们的时间,精力和预算都有限.

我理解这样的一般性答案可能并不能解决具体问题.

在这种特殊情况下,明显的用户利益在过去并不足以证明随之而来的语言的复杂性.通过严格区分不同的语言实体如何嵌套在一起,我们(1)将法律程序限制在一个易于理解的共同风格中,(2)使定义"标识符查找"规则成为可能,这些规则是可理解的,可指定的,可实现的,可测试的并且可记录.

通过将方法体限制为始终位于结构或类中,我们可以更容易地推断出在调用上下文中使用的非限定标识符的含义; 这样的东西总是当前类型(或基类型)的可调用成员.

(......)

这个后续发布:

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

(......)

像所有的设计决策一样,当我们遇到许多竞争,引人注目,有价值和不可能的想法时,我们必须找到一个可行的妥协方案.除非考虑所有可能性,否则我们不这样做,这就是我们在这种情况下所做的事情.

(重点来自原文)

  • 我不只是发布"我尊重的博客链接",我发布了一个链接到我自己的博客. (51认同)
  • @Beska,因为它显然是对原始问题的回答(而且这里唯一的答案是任何对出处的要求!)你怎么想的? (7认同)
  • 好点,但它应该是(并且是)评论,而不是答案. (4认同)
  • 也许他正试图赢得自己的尊重.也许是通过屈服于一系列痛苦的折磨.如访问SO!:) (2认同)
  • @Eric:当我们推测你的精神状态并将其作为事实呈现时,我碰巧知道你喜欢它. (2认同)

jal*_*alf 39

C#不允许它,因为Java不允许它.

我可以想到为什么Java的设计者可能不允许它的几个原因

  • Java的设计很简单.他们试图制作一种没有随机快捷方式的语言,因此你通常只有一种简单的方法来做所有事情,即使其他方法更清洁或更简洁.他们想要最小化学习曲线,并且学习"一个类可能包含方法"比"一个类可能包含方法,并且函数可能存在于类外"更简单.
  • 从表面上看,它看起来不那么面向对象.(任何不属于对象的东西显然都不能面向对象?可以吗?当然,C++说是,但是C++没有参与这个决定)

正如我在评论中已经说过的那样,我认为这是一个很好的问题,并且在很多情况下,非成员函数会更受欢迎.(这部分主要是对所有其他答案的回答,说"你不需要它")

在C++中,允许使用非成员函数,它们通常是首选,原因如下:

  • 它有助于封装.访问类的私有成员的方法越少,该类就越容易重构或维护.封装是OOP的重要组成部分.
  • 当代码不属于类时,可以更容易地重用代码.例如,C++标准库将std::findstd :: sort`定义为非成员函数,以便它们可以在任何类型的序列上重用,无论是数组,集合,链表还是(对于std :: find,at最少)流.代码重用也是OOP的重要组成部分.
  • 它为我们提供了更好的解耦.该find函数不需要知道LinkedList类,以便能够处理它.如果它已被定义为成员函数,它将是LinkedList类的成员,基本上将这两个概念合并为一个大blob.
  • 可扩展性.如果你接受一个类的接口不只是"它的所有公共成员",而且"所有在该类上运行的非成员函数",那么就可以扩展类的接口而无需编辑或甚至重新编译类本身.

具有非成员函数的能力可能起源于C(你别无选择),但在现代C++中,它本身就是一个至关重要的特性,不仅仅是为了向后兼容,而是因为它更简单它允许更清洁,更可重复使用的代码.

实际上,C#似乎已经实现了很多相同的东西,很久以后.您为什么认为添加了扩展方法?它们是实现上述目标的尝试,同时保留了简单的类似Java的语法.Lambdas也是有趣的例子,因为它们本质上是自由定义的小函数,而不是任何特定类的成员.所以是的,非成员函数的概念是有用的,C#的设计者也意识到了同样的事情.他们只是试图通过后门偷偷摸摸这个概念.

http://www.ddj.com/cpp/184401197http://www.gotw.ca/publications/mill02.htm是由C++专家撰写的关于该主题的两篇文章.

  • "C#不允许它,因为Java不允许它." - 我很想知道你是如何得出这个特殊结论的.那天你参加设计会议吗?你读过设计团队的笔记了吗?有一位设计师告诉过你吗? (55认同)
  • 当然Java对C#有影响.与C,C++,JScript,Visual Basic,Pascal以及许多其他语言一样.但是,正如许多人似乎认为的那样,C#并不是"带有愚蠢部分的Java". (31认同)
  • 绝对.C#和Java共享大量令人困惑或容易出错的习语.我们试图从Java设计的缺点中学习; 在很多地方,C#编译器会为等效的Java代码合法但有误导性的习语产生警告或错误.但我们绝不会接近所有这些. (30认同)
  • @Eric:不,我不参加那次会议.:)但是考虑到C#从Java中吸取了很多灵感(如你所说),并且C++确实允许非成员函数,并且有一些非常好的理由(除了"我们需要与C兼容") "),我真的没有想到除了"嘿,它在Java中工作,让我们坚持下去"主题的变化以外的任何原因,我发现这是最可能的解释.无论这个决定背后的原因是什么,如果Java没有首先完成它,你会考虑它吗? (14认同)
  • @Eric:微软在上个世纪末非常处于复制模式,至少是公开的.有了像J#和整个Sun/Microsoft那样的东西,就不可能否认Java影响了C#和CLI.它不会自动遵循这适用于此特定(错误)功能.但是,公共语言基础设施缺少这种通用语言功能,这一点非常值得注意. (7认同)
  • 实施细节.:) lambda表达式的概念基于闭包,而不是类成员.它可以访问在声明它的站点上可见的所有变量,因为这就是闭包的工作方式.这(概念上)不会使它成为任何事物的"成员".这就是.NET选择实现它的方式. (5认同)
  • 我们添加了扩展方法,因为它们是使查询理解作为句法而不是语义转换工作所必需的. (4认同)
  • 这里有一个很大的讽刺是,如果你看一下Java社区对Java 7中功能的典型愿望清单,其中很多都是C#中已有的东西,而且C#的经验经常被认为是将这些功能添加到Java的理由.Java肯定会从C#中吸收更多的想法中受益.遗憾的是,某些事情在旅程中稍微变得更糟,例如比较两个盒装整数用于参照相等,如果它们<= 255则它们将是相等的(即理智的对象).如果> 255,它们将不相等.语言规范甚至允许实现改变这个! (4认同)
  • @Eric:因为在我对Java有任何实际经验之前我曾经使用过C#,所以我倾向于将C#视为"带有愚蠢部分的Java".更确切地说,Java是"像C#,但血腥愚蠢,永远不会正常". (4认同)
  • 如果有人打算反驳我认为Java是愚蠢的主张,我会推荐你​​使用泛型.这足以赢得相当多的争论. (4认同)
  • @Daniel:那不是因为转移了C#功能;那是因为没有的功能:Sane`==`比较。 (2认同)

Nem*_*vic 12

非成员函数是一件好事,因为它们可以改善封装并减少类型之间的耦合.大多数现代编程语言(如Haskell和F#)都支持免费功能.

  • "静态课程"没有任何实际意义.它们就像命名空间(除了它们更难键入) - 为什么不使用真正的命名空间呢? (12认同)
  • 是啊.我所有的新项目都在C#中,我真的很想念免费功能:( (5认同)
  • 扩展方法有很多帮助,但我同意,适当的自由函数将是一个很好的补充 (3认同)

Jon*_*eet 9

将每个方法放在命名类中有什么好处?为什么非成员函数会"污染"类的接口?如果您不希望它作为类的公共API的一部分,请不要将其公开或不将其放在该类中.您始终可以创建不同的类.

我不记得曾经想要编写一个没有适当范围的方法 - 当然除了匿名函数之外(实际上并不相同).

简而言之,我看不到非成员函数的任何好处,但我可以看到将所有方法放在适当命名的类中的一致性,命名和文档方面的好处.

  • 它在C++中非常普遍,如果可能的话,非成员函数应该是首选.逻辑很简单.访问类"私有"的方法越少越好.封装得越好,维护起来就越容易.当然,它允许跨类更多的代码重用.(例如,标准库std :: find是一个单独的实现,可以重用于任何迭代器类型.在C#中,每个容器类都必须定义自己的查找函数.哪个是大多数OOP?哪个提供最多的代码可重用性? ;) (42认同)
  • 对于大多数Java/C#程序员来说,这是文化冲击的一个来源,但其背后的逻辑非常简单.例如,请参阅ddj.com/cpp/184401197.但是,我认为你很好地回答了这个问题.C#不允许它,因为非C++程序员,它看起来很奇怪,可怕和非OOP.;)它违背了许多"常规"OOP语言的教导. (10认同)
  • @Jon:但我为什么要把它放在静态类中?这让我把它放在所有课程之外有什么好处?它在概念上不属于其他静态类.但是,匿名类不是创建它们的类的成员.它们被实现为很多技巧和创建一个新的匿名类.至于课外的全局变量,没有.全局变量不能被有意义地视为类接口的一部分,即自由函数的方式.为什么你认为静态类是一个比命名空间更好的分组结构呢? (10认同)
  • 静态类如何比命名空间更小?包含3个非成员函数的命名空间是否比包含3个静态成员函数的静态类更大?除了三种方法之外,该类还包含许多其他基础结构(例如,类型id).这不会使静态类比同等命名空间更大*单位吗? (10认同)
  • "为什么你认为静态类是一个比命名空间更好的分组结构呢?" 借调!:)如果允许非成员函数(以及其他所有相等的函数),则命名空间将优于静态类,因为它可以被using使用.或者等价地我们可以问:为什么'using'指令不能以一个静态类为目标,允许我们无限制地调用它的方法? (8认同)
  • (1) - 道歉; 我认为这不符合预期(我从你那里学到了很多东西,我仍然计划用你的新书!).也就是说,时间详细说明:我们看到多少个C#项目没有"工具"/"实用工具"/"HelperClass"类?不多.恕我直言,仅这一点就打败了"阶级"本身的实际概念或理念:必须有一个共同点(例如,处理相同事物的方法),模式,共享空间,我们人类可以理解的想法,与之相关并且我们通过计算机程序中的类来表示. (5认同)
  • (4) - 最后:当一种语言实现一个新功能时,我不认为由其他语言来解释没有该功能的好处是什么; 我认为这更多是关于该语言解释所述功能的好处.就个人而言,我认为不必把所有东西放在课堂上都没有任何好处.但是,这也是所有意见...... (5认同)
  • @jalf:"更少的方法可以访问类的私有,越多越好"的论点对我不起作用 - 因为它只是建议你将成员放在不同的类中,例如静态的. (4认同)
  • 同样,课程的目的是什么?它们是对象的模板.不能用于创建对象的类不能满足其目的.命名空间或模块是用于分组相关功能的更好的机制. (4认同)
  • "不将每个方法放在命名类中有什么好处?" 耶稣,并且认为我打算买你的书...... (4认同)
  • (2) - 例如,如果我必须写一个处理动物和汽车的程序(正如我们在路上看到的那样); 我可能有两个班:"动物"和"汽车".现在,我在哪里放置我需要的其他方法来检查当前日期并将其向后移动一个月?那么,在"Utils"中.但是,在"柏拉图式"的意义上,"实用主义"这个类代表什么概念,共同点,共同点是什么?没有.每个应用程序中不可避免的不匹配在哪里?恕我直言,不得不把所有东西都放在一个班级中,简单地将OOP方法发挥到极致. (4认同)
  • (3) - 当然,除非有人给我一个很好的答案,为什么一切都应该上课.之前的情况是这样吗,用其他语言?那么,如果不是这样的话,为什么要采用这种方法呢?究竟有什么好处?备注:我知道所有这些都是哲学的,完全是主观的,许多其他人可能不同意. (4认同)
  • 顺便说一句,我认为匿名函数*几乎是相同的.从概念上讲,它们是与任何单个类无关的函数.它们表现为非成员函数.但CLR要求将它们实现为匿名类的成员.扩展方法是另一个让步的事实,有时,我们真的希望能够扩展一个类而不实际添加成员.在C++中,出于同样的原因,您使用非成员. (3认同)
  • 在扩展方法方面,我会说它们肯定支持这样的论点,即有一种方法可以在不依赖于类的基础上构建类.但是,这并不意味着不应将这些方法组合在一起 - 而且一个类是一个非常方便的分组结构,即IMO.比在命名空间级别更好.(扩展方法的一个问题是它们被发现的方式 - 我希望有类似"使用静态System.Linq.Enumerable;"来确定从何处获取扩展方法.) (2认同)
  • 我更喜欢静态类到命名空间,因为它是一个较小的单元 - 通常静态类围绕单个类型工作(例如Enumerable使用IEnumerable,Queryable使用IQueryable).我发现一个更好的单位而不仅仅是"这个命名空间中有很多东西".我同意拥有相当于Java的静态导入会很好 - 因为那是*explicit*(一次).这意味着您可以*在任何地方*使用显式表单,使方法的源清除有益的地方*或*您可以使用静态导入. (2认同)
  • @Nemanja:我不认为类只是对象的模板.我认为它们是成员的容器,包括方法......而我认为命名空间是类型的容器. (2认同)
  • @乔恩.传统上,模块是"成员"的容器 - 引入类是为了创建对象. (2认同)