Why aren't there macros in C#?

2019-01-31 16:33发布

问题:

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.

回答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



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



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



回答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)


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



回答6:

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.



回答7:

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.



回答8:

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



回答9:

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.



回答10:

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



回答11:

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