Why aren't there macros in C#?
Asked Answered
G

11

53

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.

Gahnite answered 2/9, 2009 at 19:45 Comment(12)
I can answer in three words (plus a trademark): Macros Are Evil™Berneicebernelle
Yeah, honestly, there is little reason to use macros these days in most C++ code.Frohman
Lots of good answers there, so just an aside--C# is not derived from C or C++, it was derived from Java. Java was only loosely based on C/C++ and tried to eliminate many of the bad parts (of which macros and the entire pre-processor are possibly the biggest). The naming seems to mislead people--and also the fact that the language has added features at a much quicker pace than Java...Vidavidal
Macros are great! The flexibility they allow is awesome. Sure they allow you to shoot yourself in the foot, but abhorring them completely is a mistake in my opinion.Gahnite
@Andrew: Perhaps you could show us an example of a situation where macros are a good solution? Otherwise, it seems like you're very much in the minority. Every other C++ developer hates macros. I personally can't remember the last time I even considered using a macro in C++. There are just always better solutions. Which is why other languages don't add macros.Woodpecker
@jalf: When are macros a good solution? This has already been asked and answered: https://mcmap.net/q/15744/-when-are-c-macros-beneficial-closedGahnite
@Andrew: Most of those don't apply at all to C#. In my 10+ years of industry coding, the "flexibility" of the C/C++ preprocessor is the single largest source of illegible code. C# got it right with directives 1) strictly used for conditional compilation 2) only allowing #define as the first item in a file and 3) not "leaking" directives across files.Santanasantayana
See: Extension Methods. The 'gist' of the C# team I get is they try to reach parity, but in type safe and predictable ways (just look at generics, not as versatile as templates, but much 'nicer'). Extension methods seem like a macro stop-gap to me.Smelly
1. Judging macros by languages like C++ isn't really fair, take a look at macros in LISP. 2. C# has Expression trees, dynamic types, and run-time proxies, that allow you to reinterpret code at run-time. 3. DSL's are a very powerful tool... not sure if a good one though...Conidium
Macros indeed are great for some stuff, it's a shame C# doesn't have them. For example they allow you to easily write a code that gets it into the binary only with certain compiler flags and is completely stripped out for production builds, thus enhances the flexibility and performance, but that is something (performance) what C# as well as java generally suck in. Perhaps lack of macros is one of reasons for that. (C# allows you to do similar stuff, but you need to wrap such code with #if on all places instead of being able to write 1 line code which eventually expand to something)Llovera
Those familiar with Unity have often encountered code like gameObject.GetComponent<Animator>().Play(Anim.Opening);. Now I'd have wanted to just type animator.Play(Anim.Opening) in the script of the current GameObject instance and everybody would know which Animator it's all about. This is just one example of many. And here you cannot use a method. Generally code abbreviation is a basic useful feature. It's destructive to deny an arguably useful feature just because some reckless people use it in the wrong way. This would mean that knives and other dangerous tools be ceased to be made.Homologue
Macros are a powerful way to generate repetitive code. C# projects are often riddled with boilerplate, or have it generated by external code generators which complicate the build and are basically macro systems.Bocanegra
D
73

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

Defend answered 2/9, 2009 at 19:51 Comment(6)
Thanks Hank, was wondering why it didn't look like what I had copied and pasted.Defend
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?Craigcraighead
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.Replay
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 hypocracyLlovera
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".Homologue
I think the designers of C# broke their readability rule by reflection. XAML bindings can be extremely difficult to "read".Corey
T
46

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.

Tuition answered 1/1, 2010 at 6:55 Comment(1)
At last, someone who doesn't spout the usual "Macros are Evil" nonsense.Stamps
C
28

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...

Cornia answered 2/9, 2009 at 19:48 Comment(10)
I hate #define macros. I am soooooo glad C# doesn't support such macros. I actually have never seen a good use of #define.Aekerly
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.Incentive
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. +1Defend
I voted up, but I'm going to point a minor contradiction - "I used C 12 years ago but it had no affect me".Impure
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 notChiaroscuro
@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.Blakney
@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.Trant
@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.Stamps
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.Ernaernald
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.Amoeba
B
11

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)
Boatright answered 30/6, 2012 at 13:18 Comment(12)
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.Craigcraighead
@Craigcraighead 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.Boatright
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).Craigcraighead
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.Craigcraighead
@Craigcraighead 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.Boatright
@Craigcraighead 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.Boatright
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.Craigcraighead
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.Craigcraighead
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.Craigcraighead
@Craigcraighead 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.Boatright
@Craigcraighead There you go: msdn.microsoft.com/en-us/library/…Boatright
let us continue this discussion in chatCraigcraighead
P
6

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 :)

Paulenepauletta answered 2/9, 2009 at 19:53 Comment(1)
Good point. Nothing is stopping using the C preprocessor if you really want.Matelda
D
2

As a long time C# programmer who went off to learn C++ for a while, I now miss rich support for metaprogramming C#. At least, I now have a more expansive appreciation for what metaprogramming can mean.

I would really like to see the kind of macro support that's instilled in Nemerle in C#. It seems to add a very natural and powerful extension capability to the language. If you haven't looked at it, I really recommend doing so.

There are some great examples on Wikipedia.

Dorthydortmund answered 23/9, 2012 at 20:52 Comment(0)
W
1

Macros are overused in C++ but they still have their uses, however most of these uses are not relevant in C# due to reflection and the better integrated use of exceptions for error reporting.

Water answered 2/9, 2009 at 20:36 Comment(0)
M
1

This article compares perl and lisp macros but the point is still the same: Text level macros (perl/c++) cause massive problems compared to source level macros (lisp)

http://lists.warhead.org.uk/pipermail/iwe/2005-July/000130.html

Braver people than me have rolled their own macro like system in c# http://www.codeproject.com/KB/recipes/prepro.aspx

Matelda answered 3/9, 2009 at 1:16 Comment(0)
F
0

Macros are a tool for the days when most programmers were smarter than the compiler. In C/C++, there are still some cases where this is true.

Nowdays, most programmers aren't as smart as the C# compiler/runtime.

Fireweed answered 2/9, 2009 at 20:49 Comment(2)
so are compilers getting smarter, or programmers getting dumber?Tramontane
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...Superfluity
S
0

Anyone who agrees with the idea that macros are bad should read the book, "With Folded Hands." http://en.wikipedia.org/wiki/With_Folded_Hands It tells a story about how we can keep people from doing stupid things all the way to the point of preventing them from doing very wise things.

While I like C#, I do really hate that it contributes to the stupidification of actual software engineers. So, yes, leave macros to the professionals. While we're at it, leave the naming of variables to professionals, too. That can make for some really unreadable code. To follow the full statement of "code must be ultimately readable" all variables should be named A-Z, followed by a-z (or some other arbitrary construct like only nouns). Because some unskilled person may name their variable "SomethingUsefulButNotAllowedByTheCompilerBecauseSomeUsersMayDoDumbThings".

Shorn answered 16/3, 2016 at 16:17 Comment(0)
T
-1

You can do some thing you do with macros like PropertyChanged with ways like this

If thats better than macros ? Thats a question YOU must decide :)

Torrid answered 16/7, 2015 at 6:21 Comment(0)

© 2022 - 2024 — McMap. All rights reserved.