为什么在C#中没有macros?
在第一次学习C#时,我惊讶于他们不支持C / C ++中存在的相同容量的macros。 我意识到#define关键字存在于C#中,但是与我在C / C ++中长期热爱的相比,它是非常缺乏的。 有谁知道为什么真正的macros从C#中丢失?
我很抱歉,如果这个问题已经以某种forms或其他forms提出 – 我保证我花了坚实的5分钟寻找重复之前张贴。
从C#faq。
http://blogs.msdn.com/CSharpFAQ/archive/2004/03/09/86979.aspx
为什么C#不支持#definemacros? 在C ++中,我可以定义一个macros,例如:
#define PRODUCT(x, y, z) x * y * z
然后在代码中使用它:
int a = PRODUCT(3, 2, 1);
C#不允许你这样做。 为什么?
有几个原因。 首先是可读性。
C#的主要devise目标之一是保持代码的可读性。 具有编写macros的能力使程序员能够创build自己的语言 – 不一定与下面的代码有任何关系。 为了理解代码的作用,用户不仅要理解语言是如何工作的,还必须理解当时有效的所有#definemacros。 这使代码更难以阅读。
在C#中,可以使用方法而不是macros,而且在大多数情况下,JIT将内联它们,从而为您提供相同的性能方面。
还有一个更微妙的问题。 macros是以文本方式完成的,这意味着如果我写:
int y = PRODUCT (1 + 2, 3 + 4, 5 + 6)
我希望得到的东西给我
3 * 7 *11 = 231
,但实际上,我已经定义的扩展给出了:
int y = 1 + 2 * 3 + 4 * 5 + 6;
这给了我33.我可以通过明智地应用括号来解决这个问题,但是很容易写出一个在某些情况下有用而不在其他情况下使用的macros。
尽pipeC#并不严格地说有一个预处理器,但它确实有条件编译符号,可以用来影响编译。 这些可以在代码中定义,也可以用编译器的参数定义。 C#中的“预处理”指令(仅仅为了与C / C ++保持一致,尽pipe没有单独的预处理步骤)(文本摘自ECMA规范):
#define and #undef
用于定义和取消定义条件编译符号
#if, #elif, #else and #endif
用于有条件地跳过部分源代码
#line
用于控制发出错误和警告的行号。
#error and #warning
用于发布错误和警告。
#region and #endregion
用于明确标记源代码的部分。
有关上述的更多信息,请参阅ECMA规范的第9.5节。 条件编译也可以通过在方法中使用条件属性来实现,所以只有在定义了适当的符号的情况下才能编译调用方法。 有关详细信息,请参阅ECMA规范的第24.4.2节。
作者:Eric Gunnerson
所以你可以一遍又一遍的打字。
// Windows presetation foundation dependency property. public class MyStateControl : ButtonBase { public MyStateControl() : base() { } public Boolean State { get { return (Boolean)this.GetValue(StateProperty); } set { this.SetValue(StateProperty, value); } } public static readonly DependencyProperty StateProperty = DependencyProperty.Register( "State", typeof(Boolean), typeof(MyStateControl),new PropertyMetadata(false)); }
很明显,C#和.NET的devise者实际上从来没有使用任何他们创build的库或框架。 如果他们这样做了,他们会意识到,某种forms的hygenic句法macros观系统肯定是有序的。
不要让C和C ++的蹩脚macros的缺点让你在编译时间parsing代码的能力上变得糟糕。 编译时间parsing和代码生成使您能够更有效地expression代码的意义和意图,而不必详细说明所有源代码的细节。 例如,如果你可以用这个replace上面的代码:
public class MyStateControl : ButtonBase { public MyStateControl() : base() { } [DependencyProperty(DefaultValue=true)] bool State { get; set; } }
Boo有他们,OcamML(至lessMeta ML)有他们,C和C ++有他们(在一个讨厌的forms,但总比没有他们好)。 C#没有。
在我的经验中,C ++风格的macros增加了巨大的复杂性而没有相应的好处。 我当然也没有在C#或Java中错过它们。 (我很less在C#中使用预处理器符号,但偶尔我也很高兴他们在那里。)
现在各种各样的人都要求Lisp风格的macros ,我对它的了解不多,但肯定比C ++风格更令人愉快。
你特别想用macros做什么? 我们可能能够帮助你以更通俗的C#方式思考
C#针对更广泛的受众群体(或其他用户群),而不是C ++,C或ASM。 实现这一目标的唯一方法是让程序员相当不熟练。 因此,所有有力而危险的工具都被夺走了。 即macros,多重inheritance,控制对象的生命期或types不可知的编程。
用同样的方式,火柴和猎枪是有用和必要的,但它们必须放在儿童不能接触的地方。 (不幸的是,纵火,谋杀,内存泄漏和不可读的代码仍然会发生)。
在指责我没有考虑C#之前,你曾经写过多less次:
protected int _PropOne; public int PropOne { get { return _PropOne; } set { if(value == _PropOne) { return; } NotifyPropertyChanging("PropOne"); _PropOne = value; NotifyPropertyChanged("PropOne"); } }
有了macros,每一次这16行看起来都是这样的:
DECLARE_PROPERTY(int, PropOne) DECLARE_PROPERTY(string, PropTwo) DECLARE_PROPERTY(BitmapImage, PropThree)
C / C ++中的macros被用来定义常量,产生小的内联函数,以及与编译代码(#ifdef)直接相关的各种东西。
在C#中,你有强types的常量,一个足够聪明的编译器来在必要时内联函数,并且知道如何以正确的方式编译东西(没有预编译的头文件废话)。
但是没有什么特别的原因,为什么你不能通过C预处理器首先运行你的CS文件,如果你真的想:)
作为一个很长一段时间学习C ++的C#程序员,我现在错过了对C#元编程的丰富支持。 至less,我现在对元编程的含义有了更广泛的认识。
我真的很想看到在C# 中用Nemerle灌输的那种macros观支持。 它似乎增加了一种非常自然和强大的扩展能力的语言。 如果你没有看过,我真的推荐这样做。
维基百科上有一些很棒的例子。
macros在C++
被过度使用,但它们仍然有其用处 ,但是由于reflection以及错误报告exception的更好集成使用,大多数这些用法在C#
中并不相关。
本文比较perl和lispmacros,但点仍然是相同的:与源级macros(lisp)相比,文本级macros(perl / c ++)会导致大量问题
http://lists.warhead.org.uk/pipermail/iwe/2005-July/000130.html
比我更勇敢的人已经在c#中推出了自己的macros像系统http://www.codeproject.com/KB/recipes/prepro.aspx
当大多数程序员比编译器更聪明的时候,macros是一种工具。 在C / C ++中,仍然有一些情况是这样的。
现在,大多数程序员不像C#编译器/运行时那么聪明。
任何认同macros观不好的观点的人都应该阅读这本书“用双手”。 http://en.wikipedia.org/wiki/With_Folded_Hands它讲述了一个故事,我们如何防止人们做愚蠢的事情一直到阻止他们做出非常明智的事情。;
虽然我喜欢C#,但是我真的很讨厌这会造成实际的软件工程师的愚蠢。 所以,是的,把macros留给专业人士。 当我们在这个时候,也把variables的名字留给专业人士。 这可能会导致一些非常不可读的代码。 要遵循“代码必须最终可读”的完整声明,所有variables应该被命名为AZ,然后是az(或者其他任意的结构,如只有名词)。 因为一些不熟练的人可能会将其variables命名为“SomethingUsefulButNotAllowedByTheCompilerBecauseSomeUsersMayDoDumbThings”。
你可以用像PropertyChanged这样的方法做一些你所做的事情
如果这比macros更好? 这是一个问题,你必须决定:)