为什么C#中没有宏?

And*_*son 48 .net c# macros c-preprocessor

在第一次学习C#时,我很惊讶他们不支持与C/C++相同容量的宏.我意识到#define关键字存在于C#中,但与我在C/C++中的喜爱相比,它非常缺乏.有谁知道为什么C#中缺少真正的宏?

如果这个问题已经以某种形式提出,我很抱歉 - 我保证在发布前花了5分钟时间寻找重复的问题.

Dou*_*asH 67

来自C#faq.

http://blogs.msdn.com/CSharpFAQ/archive/2004/03/09/86979.aspx

为什么C#不支持#define宏?在C++中,我可以定义一个宏,例如:

#define PRODUCT(x, y, z) x * y * z

然后在代码中使用它:

int a = PRODUCT(3, 2, 1);

C#不允许你这样做.为什么?

有几个原因.第一个是可读性.

我们对C#的主要设计目标之一是保持代码的可读性.具有编写宏的能力使程序员能够创建自己的语言 - 这种语言不一定与下面的代码有任何关系.要理解代码的作用,用户不仅必须了解语言的工作方式,还必须了解当时有效的所有#define宏.这使得代码更难阅读.

在C#中,您可以使用方法而不是宏,并且在大多数情况下,JIT将内联它们,为您提供相同的性能方面.

还有一个更微妙的问题.宏以文本方式完成,这意味着如果我写:

int y = PRODUCT (1 + 2, 3 + 4, 5 + 6)

我希望能得到一些东西给我3 * 7 *11 = 231,但实际上,我所定义的扩展给出了:

int y = 1 + 2 * 3 + 4 * 5 + 6;

这给了我33.我可以通过明智的括号应用来解决这个问题,但很容易编写一个在某些情况下工作而不在其他情况下工作的宏.

虽然C#严格来说没有预处理器,但它确实有条件编译符号可用于影响编译.这些可以在代码中定义,也可以在编译器的参数中定义.C#中的"预处理"指令(仅为与C/C++的一致性命名,尽管没有单独的预处理步骤)是(来自ECMA规范的文本):

#define and #undef 用于定义和取消定义条件编译符号

#if, #elif, #else and #endif

用于有条件地跳过部分源代码

#line 用于控制为错误和警告发出的行号.

#error and #warning 用于发出错误和警告.

#region and #endregion

用于显式标记源代码的各个部分.

有关上述内容的更多信息,请参阅ECMA规范的第9.5节.也可以使用方法上的Conditional属性来实现条件编译,以便只有在定义了适当的符号时才会编译对方法的调用.有关详细信息,请参阅ECMA规范的第24.4.2节.

作者:Eric Gunnerson

  • 在这样的问题上,上帝的话永远是好的.+1 (7认同)
  • 您可能想要转义某些格式:\# (4认同)
  • 非常有趣的是,c#禁止c像宏一样,因为它们是邪恶的,但用C语言编写的非常CIL解释器在源代码中使用它们......这是一个什么样的伪君子 (4认同)
  • 我每天都感谢上帝,让我不必调试带有大量晦涩宏的程序。是否有任何理由使用它们而简单的良好编程实践还不够? (2认同)
  • 这是一个: WinError.h 。如果我希望能够确定 C# 中的 WIN32 错误意味着什么,我必须在某处找到 WinError.h 的副本,将其破解到一个包含数千个常量的 C# 类中(或者找到这样一个已经被破解的类, ) 并处理我的输出程序集刚刚从 45k 增长到 180k 的事实,因为在带有预处理器的语言中会添加恰好 0k。此外,如果将来为更新(或其他不同)版本的 Windows 构建此代码,我必须找到它的 WinError.h 并使用类似的代码大小跳转类似地破解它。 (2认同)

小智 36

这样你就可以一遍又一遍地打字.

// 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));
}
Run Code Online (Sandbox Code Playgroud)

显然,C#和.NET的设计者实际上从未使用他们创建的任何库或框架.如果他们这样做了,他们就会意识到某种形式的hygenic句法宏观系统肯定是有序的.

不要让C和C++的蹩脚宏的缺点让你厌倦了编译时解析代码的力量.编译时间分辨率和代码生成使您可以更有效地表达代码的意义和意图,而无需详细说明源代码的所有细节.例如,如果您可以用以下内容替换上面的内容:

public class MyStateControl : ButtonBase
{
  public MyStateControl() : base() { }

  [DependencyProperty(DefaultValue=true)] 
  bool State { get; set; }
}
Run Code Online (Sandbox Code Playgroud)

Boo有它们,OcamML(至少是Meta ML)有它们,而C和C++有它们(以一种讨厌的形式,但总比没有它们好).C#没有.

  • 终于有人不再滔滔不绝地说“宏是邪恶的”废话了。 (6认同)

Jon*_*eet 28

根据我的经验,C++风格的宏添加了大量的复杂性而没有相应的好处.我当然没有在C#或Java中错过它们.(我很少在C#中使用预处理器符号,但我偶尔会很高兴他们在那里.)

现在各种各样的人都要求使用Lisp风格的宏,我对它知之甚少,但肯定听起来比C++ 风格的宏更令人愉快.

你特别想用宏什么?我们或许可以帮助您以更加惯用的C#方式思考......

  • 我讨厌#define宏.我很高兴C#不支持这样的宏.我实际上从未见过#define的好用. (2认同)
  • @Chris:你能详细说说吗? (2认同)

Age*_*t_L 9

与C++,C或ASM相比,C#面向更广泛的受众(或其他术语,消费者群体).实现这一目标的唯一方法是让程序员接受相当不熟练的技能.因此,所有强大但危险的工具都被带走了.即宏,多重继承,对对象生命周期的控制或类型无关的编程.

以同样的方式匹配,刀和钉枪是有用和必要的,但它们必须远离儿童.(遗憾的是,纵火,谋杀,内存泄漏和不可读的代码仍然会发生).

在指责我不考虑C#之前,有多少次你写过:

protected int _PropOne;
public int PropOne
{
    get
    {
        return _PropOne;
    }
    set
    {
        if(value == _PropOne) { return; }
        NotifyPropertyChanging("PropOne");
        _PropOne = value;
        NotifyPropertyChanged("PropOne");
    }
}
Run Code Online (Sandbox Code Playgroud)

使用宏,每次这16行看起来像这样:

DECLARE_PROPERTY(int, PropOne)
DECLARE_PROPERTY(string, PropTwo)
DECLARE_PROPERTY(BitmapImage, PropThree)
Run Code Online (Sandbox Code Playgroud)

  • @weberc2 抱歉,伙计,但你刚刚证明了自己是反宏狂热分子,即使被打在脸上,也否认它的价值。我无法与那些声称调试宏比调试字符串更难的人争论——据我所知,这是根本不可能的。 (2认同)

Set*_*eth 6

C / C++ 中的宏用于定义常量、生成小型内联函数以及与编译代码直接相关的各种操作 (#ifdef)。

在 C# 中,您拥有强类型常量、足够智能的编译器,可以在必要时内联函数,并且知道如何以正确的方式编译内容(没有预编译头废话)。

但是,如果您确实想要的话,没有什么特殊原因不能首先通过 C 预处理器运行 CS 文件:)

  • 好点子。如果您确实需要,没有什么可以阻止使用 C 预处理器。 (3认同)