Why aren't there macros in C#?

28,216

Solution 1

from the C# faq.

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

Why doesn't C# support #define macros? In C++, I can define a macro such as:

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

and then use it in code:

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

C# doesn't allow you to do this. Why?

There are a few reasons why. The first is one of readability.

One of our main design goals for C# is to keep the code very readable. Having the ability to write macros gives the programmer the ability to create their own language - one that doesn't necessarily bear any relation to what the code underneath. To understand what the code does, the user must not only understand how the language works, but he must also understand all of the #define macros that are in effect at that point in time. That makes code much harder to read.

In C#, you can use methods instead of macros, and in most cases, the JIT will inline them, giving you the same performance aspect.

There's also a somewhat more subtle issue. Macros are done textually, which means if I write:

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

I would expect to get something that gives me 3 * 7 *11 = 231, but in fact, the expansion as I've defined it gives:

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

which gives me 33. I can get around that by a judicious application of parenthesis, but its very easy to write a macro that works in some situations and not in others.

Although C# doesn't strictly speaking have a pre-processor, it does have conditional compilation symbols which can be used to affect compilation. These can be defined within code or with parameters to the compiler. The "pre-processing" directives in C# (named solely for consistency with C/C++, despite there being no separate pre-processing step) are (text taken from the ECMA specification):

#define and #undef Used to define and undefine conditional compilation symbols

#if, #elif, #else and #endif

Used to conditionally skip sections of source code

#line Used to control line numbers emitted for errors and warnings.

#error and #warning Used to issue errors and warnings.

#region and #endregion

Used to explicitly mark sections of source code.

See section 9.5 of the ECMA specification for more information on the above. Conditional compilation can also be achieved using the Conditional attribute on a method, so that calls to the method will only be compiled when the appropriate symbol is defined. See section 24.4.2 of the ECMA specifcation for more information on this.

Author: Eric Gunnerson

Solution 2

So that you can have fun typing THIS over and over and over again.

// 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));
}

Obviously the designers of C# and .NET never actually use any of the libraries or frameworks they create. If they did, they would realize that some form of hygenic syntactic macro system is definitely in order.

Don't let the shortcomings of C and C++'s lame macros sour you on the power of compile time resolved code. Compile time resolution and code generation allows you to more effectively express the MEANING and INTENT of code without having to spell out all of the niggling details of the source code. For example, what if you could replace the above with this:

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

  [DependencyProperty(DefaultValue=true)] 
  bool State { get; set; }
}

Boo has them, OcamML (at least Meta ML) has them, and C and C++ has them (in a nasty form, but better than not having them at all). C# doesn't.

Solution 3

C++-style macros add a huge amount of complexity without corresponding benefit, in my experience. I certainly haven't missed them either in C# or Java. (I rarely use preprocessor symbols at all in C#, but I'm occasionally glad they're there.)

Now various people have called for Lisp-style macros, which I know little about but certainly sound rather more pleasant than C++-style ones.

What do you particularly want to do with macros? We may be able to help you think in a more idiomatically C# way...

Solution 4

C# is aimed at wider audience (or in other term, consumer base) than C++, C or ASM. The only way of achieving this goal is reaching programmers considerably less skilled. Therefore, all the powerful but dangerous tools are taken away. I.e. macros, multiple inheritance, control over object lifetime or type-agnostic programming.

In a very same way matches, knives and nailguns are useful and necessary, but they have to be kept out of reach of children. (sadly, arsons, murders, memory leaks and unreadable code still do happen).

And before accusing me of not thinking C#, how many times have you wrote that:

protected int _PropOne;
public int PropOne
{
    get
    {
        return _PropOne;
    }
    set
    {
        if(value == _PropOne) { return; }
        NotifyPropertyChanging("PropOne");
        _PropOne = value;
        NotifyPropertyChanged("PropOne");
    }
}

With macros, every time those 16 lines would look like that:

DECLARE_PROPERTY(int, PropOne)
DECLARE_PROPERTY(string, PropTwo)
DECLARE_PROPERTY(BitmapImage, PropThree)

Solution 5

Macros in C / C++ were used to define constants, produce small inline functions, and for various things directly related to compiling the code (#ifdef).

In C#, you have strongly typed constants, a smart enough compiler to inline functions when necessary, and knows how to compile stuff the right way (no precompiled header nonsense).

But there's no particular reason why you couldn't run your CS file through the C preprocessor first if you really wanted to :)

Share:
28,216
Andrew Garrison
Author by

Andrew Garrison

Updated on June 15, 2020

Comments

  • Andrew Garrison
    Andrew Garrison almost 4 years

    When learning C# for the first time, I was astonished that they had no support for macros in the same capacity that exists in C/C++. I realize that the #define keyword exists in C#, but it is greatly lacking compared to what I grew to love in C/C++. Does anyone know why real macros are missing from C#?

    I apologize if this question is already asked in some form or another - I promise I spent a solid 5 minutes looking for duplicates before posting.

  • Bobby Cannon
    Bobby Cannon over 14 years
    I hate #define macros. I am soooooo glad C# doesn't support such macros. I actually have never seen a good use of #define.
  • DouglasH
    DouglasH over 14 years
    Thanks Hank, was wondering why it didn't look like what I had copied and pasted.
  • David Thornley
    David Thornley over 14 years
    There are plenty of good (or at least necessary) uses of #define in C. In C++, they're primarily useful to support conditional compilation, and any other use is probably a bad idea.
  • DouglasH
    DouglasH over 14 years
    Jon, a fully working code gen built into the language, what a novel concept:) hopefully one that is not designed to output exactly C# as the Codedom is now. +1
  • MordechayS
    MordechayS over 14 years
    I voted up, but I'm going to point a minor contradiction - "I used C 12 years ago but it had no affect me".
  • Matthew Lock
    Matthew Lock over 14 years
    Good point. Nothing is stopping using the C preprocessor if you really want.
  • Peter Burns
    Peter Burns about 14 years
    so are compilers getting smarter, or programmers getting dumber?
  • weberc2
    weberc2 over 11 years
    I thank the Lord every day that I don't have to debug a program with copious quantities of obscure macros. Is there any reason to use them for which simple good-programming-practices wouldn't suffice?
  • weberc2
    weberc2 over 11 years
    it seems your definition of "skilled" implies not making mistakes. If that's the case, a "skilled" programmer wouldn't leverage high level programming constructs, and would be working with raw addresses rather than variables. While the code you provided is certainly simpler, it will defeat most debugging tools. Please note that your final point hinges upon writing the macro "properly" in the first place. If we're capable of writing "proper" code all the time, then we wouldn't make a mistake for a "proper" macro to catch. At least without macros, we can quickly debug.
  • Agent_L
    Agent_L over 11 years
    @weberc2 Not at all. "Skilled" in my dictionary means "capable of writing readable code", including bug-free macros and knowing when to use them and when not. The "skill" of writing a macro is to write the code, debug it and then extract it into a macro. Macros are for extracting code that is 100% dependable and need not to be debugged. I assure you that macros are not a problem when debugging, unless specifically targeted at making code unreadable. And you're wrong: debugging mistaken property name in string NotifyPropertyChanging is difficult. This is a real world example.
  • weberc2
    weberc2 over 11 years
    Code readability is for novices, not just macro-using experts. Macros literally insert one language into another language, and to make matters worse, the two aren't easily distinguishable. Moreover, even a skilled programmer makes mistakes, meaning your macros may still need to be debugged (even if your macro doesn't contain the mistake, we often don't know that until it's been examined). Given that macros are difficult to difficult to debug. Even in your example, it's easier to read and debug the C# code (as the debugger can access it) than your macro (which a debugger can't access).
  • weberc2
    weberc2 over 11 years
    furthermore, it looks like the C# code in your example can be simplified by putting the contents of the set{} into a static method that encapsulates the checking. Then you don't have to deal with tedious macros and everyone looking at the code can still easily understand what's going on. Using macros is almost always more costly than beneficial, although I'm sure there are dozen of examples where this isn't the case.
  • Agent_L
    Agent_L over 11 years
    @weberc2 It seems that we just calculate costs differently. If you know ANY method of simplifying NofityPropertyChanged (other than VS code generating templates) please do tell me, because this overhead is killing me and everybody else I work with. I'm sorry but your suggestion about static method is nonsense. The problem lies in tying string containing property name with the name of property itself. As far as my knowledge goes, that's not possible without macros.
  • Agent_L
    Agent_L over 11 years
    @weberc2 Sorry, mate, but you've just proven yourself to be antimacro fanatic who denies it's value even when hit with it in the face. I can't argue with someone who claims that debugging a macro is harder than debugging a string - which is not possible at all, as far as I know.
  • weberc2
    weberc2 over 11 years
    I'm not particularly familiar with NotifyPropertyChanged; however, since it uses a string to identify a property, I would think there must be a better way of providing said notification. If I'm right, the macro is still just another instance of using bad practice to try to compensate for bad practice.
  • weberc2
    weberc2 over 11 years
    I'm sorry, you're still incorrect. Macros are still just another shortcut feature that do more harm than good. When you're using them to skirt the quality-enforcing constraints of a language, you're doing it wrong. Providing for macros sabotages the reliability of those constraints. If you're going to invest the time into developing a macro to add safety to a poorly-designed function, you're better off redesigning/writing the function.
  • weberc2
    weberc2 over 11 years
    There are a handful of scenarios (e.g. conditional compilation) for which default language features can't be the answer, and for which I don't know of better solutions than macros (i.e., I haven't spent any time trying to think of better alternatives), but in 99.9% of use cases, macros are the wrong tool, and it's astronomically better that they aren't provided for in the first place. For the other 0.01% of scenarios, you can always write in the macro and use a preprocessor.
  • Agent_L
    Agent_L over 11 years
    @weberc2 I am talking about INotifyPropertyChanged interface and its PropertyChangedEventArgs which has defined PropertyName as string by Microsoft. It's absolute basics, required in any app with dynamic user interface written with Forms (or Linq2SQL). Since you're not familiar with it, then why are you participating in a discussion on a topic you have no idea about? What you don't know is that there are hundreds of such blocks in most applications, and slightest error in "PropNameOne" generates elusive and absolutely undebuggable problem.
  • Agent_L
    Agent_L over 11 years
    @weberc2 There you go: msdn.microsoft.com/en-us/library/…
  • weberc2
    weberc2 over 11 years
  • Blair Davidson
    Blair Davidson almost 11 years
    Macros enable or support certain tricks in c / c++ but they are really just a sign of the developer wants something supported by the compiler which currently is not
  • reirab
    reirab over 10 years
    Here's one: WinError.h . If I want to be able to determine what a WIN32 error means in C#, I have to go find a copy of WinError.h somewhere, hack it into a C# class with literally thousands of constants (or find such an already-hacked class,) and deal with the fact that my output assembly just grew from 45k to 180k because of something that would have added exactly 0k in a language with a preprocessor. Furthermore, if this code gets built in the future for a newer (or otherwise different) version of Windows, I have to go find its WinError.h and hack it similarly with a similar code size jump.
  • user1132959
    user1132959 about 10 years
    @BobbyCannon There are plenty of good uses of #define in the Linux Kernel, OpenCV, etc. For example, in OpenCV, if you want to add a test case, you use the macro TEST and define a function for the test. I can see your preference is without macros, but there are definitely good uses for it in my opinion.
  • Petr
    Petr almost 10 years
    it's very funny that c# forbids c like macros because they are evil, but a very CIL interpretor, written in c uses them in source code... what a hypocracy
  • Dolda2000
    Dolda2000 over 9 years
    @BlairDavidson: I agree, but that is precisely the point with macros. You can't expect the compiler to support everything you may want in advance.
  • mireazma
    mireazma about 8 years
    Forgive my blasphemy but I find accessories just as evil as macros; you think you merely access a field. So you go using this mindlessly. Then you find out that in fact there's tens times more worth of computation stuff going on in the getter/setter. And this is sometimes justified exclusively by "good practice".
  • Петър Петров
    Петър Петров about 8 years
    Both of them. "Smart" compilers make dump developers. And yes, while C# is easy language, Memory management for a native P/INVOKE library is like hell...
  • Sam Hobbs
    Sam Hobbs over 7 years
    I think the designers of C# broke their readability rule by reflection. XAML bindings can be extremely difficult to "read".
  • StephenG - Help Ukraine
    StephenG - Help Ukraine almost 6 years
    At last, someone who doesn't spout the usual "Macros are Evil" nonsense.
  • StephenG - Help Ukraine
    StephenG - Help Ukraine almost 6 years
    @BlairDavidson Actually macros are something the language and compiler supports in C/C++ so the develop is just sensibly using tools they have available. Macros are part of those languages, not a kludge bolted on.
  • Haakon Løtveit
    Haakon Løtveit over 5 years
    Well, in Java we use preprocessor tags for compiler fun with things like Lombok. If instructing the compiler was officially supported, Lombok could do all its magic hackery much easier, and you could do it too in a simple manner.
  • MicroservicesOnDDD
    MicroservicesOnDDD over 3 years
    C/C++ macros are a souped-up text replace. Lisp-style macros are a replace on the AST, doable w/ Roslyn. But to realize the full power of Lisp-macros, you may have to enable a temp. override of C# syntax, & also enable the full language at macro-time. This is the difference between a language that is more static to begin with, & a language meant to be extended in just about any way, enabling much easier DSL creation and code-as-data, which is what we want to do with macros. But I want to be able to turn that paradigm on or off at will per file & use that great code-gen power in a Spartan way.