81

Is C# able to define macros as is done in the C programming language with pre-processor statements? I would like to simplify regular typing of certain repeating statements such as the following:

Console.WriteLine("foo");
Brian Tompsett - 汤莱恩
  • 5,753
  • 72
  • 57
  • 129
  • 33
    By the way, for this particular method you can write `cw` in Visual Studio and press Tab. – MasterMastic Mar 01 '13 at 20:31
  • 2
    There is a Lisp-style macro processor for C# called [LeMP](http://ecsharp.net/lemp); Lisp-style macros are much superior to C/C++ macros. But in new versions of C# you can shorten `Console.WriteLine` to `WriteLine` after adding `using static System.Console;` at the top of a source file. – Qwertie Jul 19 '18 at 03:17

9 Answers9

62

No, C# does not support preprocessor macros like C. Visual Studio on the other hand has snippets. Visual Studio's snippets are a feature of the IDE and are expanded in the editor rather than replaced in the code on compilation by a preprocessor.

Antonio
  • 19,451
  • 13
  • 99
  • 197
Andrew Hare
  • 344,730
  • 71
  • 640
  • 635
  • 34
    @weberc2 bad programmers shouldn't spoil elegant functionality for the rest of us – Jake May 30 '13 at 15:44
  • 25
    @Jake Macros aren't "elegant functionality", they're a compensation for poor language design. – weberc2 May 30 '13 at 18:34
  • 86
    @weberc2 Macros are very helpful when the same code base has to target multiple compile-time targets such as iOS, Android, MacOSX, etc. The lack of macros means that you have to put a lot of code between `#if/#elseif/#else` pragmas when with some simple macros one could emit the proper code for each target in an efficient/elegant and maintainable way. In short, C style macros are very helpful when you have to include code that simply will not compile on some targets. – JohnnyLambada Aug 29 '13 at 22:42
  • 4
    @JohnnyLambada The better solution is to abstract your platform-dependent code behind a common interface and use CMake and friends to make sure the correct code gets compiled for a given target. Hell, you can even do that in C. – weberc2 Sep 03 '13 at 04:29
  • 23
    @weberc2 The problem with this 'better solution' is that it's heavy and spreads what could be a few simple lines of code over multiple files which must all be understood to understand the complete solution. I don't agree that that is better. Yes, the C preprocessor was abused to the point of torment (I was definitely an abuser!), but it was also very useful in many situations to make code much simpler and easier to understand. – JohnnyLambada Sep 03 '13 at 18:12
  • 2
    @JohnnyLambada I strongly disagree. It actually does exactly the opposite of what you described: it takes a lot of preprocessor garbage scattered across a many files (files which are fundamentally unrelated to target concerns) and moves them to a few files responsible for target configuration. I don't know how anyone could think code littered with `#ifdef WIN32...` is more readable than code that isolates its conditional compilation concerns to a few sane places... – weberc2 Sep 03 '13 at 18:33
  • 8
    @weberc2 We're talking about two different things. I'm not saying that a bunch of `#ifdefs` surrounding large blocks of code is good. It's not. I'm saying that judicial use of `#define MACRO(x,y)` embedded within `#ifdef PLATFORM1 #elif PLATFORM2 ...` can be useful in limited cases as a tool in the toolbox. Perhaps you disagree with this as well -- after doing a google search in defense of the preprocessor seems most of the world does as well. But I did come across [this gem.](http://stackoverflow.com/a/653028/9648) – JohnnyLambada Sep 04 '13 at 14:29
  • 5
    @JohnnyLambada If you really want to use a preprocessor while minimizing your pain, don't define a macro with your platform logic or you'll have to deal with debugging/reading that macro all over your code base. Instead, in as few files as practical, define functions that encapsulate your preprocessor logic so you don't have to worry about it everywhere else. However, it's still better to use your build system to conditionally compile files rather than a preprocessor. – weberc2 Sep 04 '13 at 15:16
  • 3
    @Jake It's not because you've never encoutered a situation where macros are absolutely necessary that such situations don't exist. That's actually what brought me here, and let me tell you how useless your comment is... – Virus721 Jan 21 '16 at 12:22
  • 2
    @Virus721 It probably looks useless out of context. I originally replied to a comment that [weberc2](http://stackoverflow.com/users/483347/weberc2) made a few years ago, and he has since deleted it. His original comment was, "Thank God for no macros. – weberc2 Oct 25 '12 at 20:27" – Jake Jan 21 '16 at 20:46
  • 3
    I know this thread has been quiet for years, but I just had to interject. There are some things that macros can do which I don't think have any alternate solution - C/C++ style macros do things that function calls simply cannot - i.e. forcibly inline code into a function with access to the full stack frame of the scope they are in. Combined with the fact that macros also allow the compile time generation of code because macro params can be converted to quoted strings & concatenated as well as being used 'plain' which affords the writing of code in C/C++ not possible AFAIK in C# – darbotron Jun 12 '16 at 19:53
  • 1
    I imagine that much of what I'm saying isn't possible is technically possible, but the code required is so unwieldy and clunky & involves so many reflection calls that it feels kinda silly to call it "better". Incidentally, I'm not saying that C# is better or worse that C/C++; but there are many times when I have encountered situations that could be trivially solved with a macro in C/C++ with little or no run-time overhead which took tonnes of messing about with reflection, debug stack objects etc. and so adds a significant & unnecessary GC churn overhead to a debug build using it. – darbotron Jun 12 '16 at 20:00
  • 4
    For those pretending macros are bad: C# projects use plenty of "macros" they just come in the form of code-generators that run outside the compiler. However, external code-generators complicate the build and create even more non-standard DSLs. I'd much rather have the ability to write a compiler macro to emit repetitive code blocks than resort to writing an external code generator. There are also many things a macro can do that a function can't.(extract __line__, generate boilerplate) That said, I'd prefer my macros to be syntactic macros, more like Nemerle or Boo, not like the C preprocessor. – David Jeske Jun 15 '16 at 02:34
  • 2
    Macros allow to use super fast, but ugly inlining to optimize performance critical parts of the code. Consider some calculations on `Bgra32` pixels: any function splitting such pixel to ARGB components will be way slower than bitwise operations coded directly. But direct coding of such operations would be way less readable than using a macro like `_G(pixel)`. – Harry Dec 02 '16 at 09:40
  • 3
    snippets insert code while editing rather than at compile-time, so editing a snippet doesn't change the code when you re-compile. – Kevin S. Miller Mar 06 '20 at 19:38
  • 3
    snippets are not a replacement for macros. I am not sure why so many people upvoted this... – user1164199 Aug 26 '20 at 16:26
  • @weberc2 macros would be useful for my Unity project that wants to import native functions. Instead I have to write `#ifdef UNITY_IPHONE \n [DllImport("__Internal") \n #else \n [DllImport("MyLib")] \n #endif` before each imported function. I could just use a macro for this kind of stuff – IC_ Jul 23 '23 at 03:18
  • @IC_ Those #ifdefs are macros, and there are better ways to do conditional compilation. Go's approach is limited but clean. If you have to do macros, don't do textual macros like C and instead do syntactic macros like Rust. My opinions haven't changed in the intervening decade, but I probably would have phrased my comment differently today. :) – weberc2 Jul 24 '23 at 21:11
51

You can use a C preprocessor (like mcpp) and rig it into your .csproj file. Then you chnage "build action" on your source file from Compile to Preprocess or whatever you call it. Just add BeforBuild to your .csproj like this:

  <Target Name="BeforeBuild" Inputs="@(Preprocess)" Outputs="@(Preprocess->'%(Filename)_P.cs')">
<Exec Command="..\Bin\cpp.exe @(Preprocess) -P -o %(RelativeDir)%(Filename)_P.cs" />
<CreateItem Include="@(Preprocess->'%(RelativeDir)%(Filename)_P.cs')">
  <Output TaskParameter="Include" ItemName="Compile" />
</CreateItem>

You may have to manually change Compile to Preprocess on at least one file (in a text editor) - then the "Preprocess" option should be available for selection in Visual Studio.

I know that macros are heavily overused and misused but removing them completely is equally bad if not worse. A classic example of macro usage would be NotifyPropertyChanged. Every programmer who had to rewrite this code by hand thousands of times knows how painful it is without macros.

user2224389
  • 520
  • 4
  • 3
  • 22
    You have to get some credit for an 'innovative' solution, and for thinking outside the box. But just a word of advice to anyone reading this 'solution', please don't do it. There are some things that are just *wrong*, that are such a hack. This is one of those things. – danpalmer Jul 30 '13 at 08:55
  • 16
    @danpalmer Why would adding a CPP shellout to `MyCommon.targets` be just *wrong*? There are just some things that CPP can do that the language makes incredibly difficult. IMO, it is worse that the developer has to actually manually stringify an argument name when writing `throw new ArgumentNullException("argumentName");`. Code contracts was supposed to solve that, but it requires an IL rewriter bolted on using `.targets`, should be no worse to bolt on a CPP call. – binki Feb 11 '14 at 22:10
  • As this post is from 2013, I would like to remind of C#6's `nameof(argumentX)`. C# 6 and 7 added a lot of profound and straight forward solutions to trivial problems which earlier caused pain, like this one. And I honestly have to agree with @danpalmer - yes, the more hacks like this are added, the less likely anyone will want to maintain a project or even get it compiled a few years later. – bytecode77 Jul 10 '17 at 13:18
  • 1
    Until C# will implement macros, this is by far the best solution, in fact, a no brainer, a bull's eye shot. I can't qualify this enough. The fact that it's not used by the majority has nothing to do with it being *the perfect* solution. BTW, IMHO this should be the adopted ubiquitously. – mireazma Jan 09 '18 at 20:38
  • 1
    @mireazma Intellisense stops working though. So it's not quite a no brainer – Aykhan Hagverdili Sep 05 '20 at 16:59
  • Is there a way to make Intellisense still work with this? – JamesHoux Oct 29 '20 at 03:14
  • I was looking into that when I had to use an old m4 preprocessor to work. It MAY be possable now as CPP switched to a token basid for macros as C# has done all this time. – Paul Bruner Jul 15 '22 at 22:05
34

I use this to avoid Console.WriteLine(...):

public static void Cout(this string str, params object[] args) { 
    Console.WriteLine(str, args);
}

and then you can use the following:

"line 1".Cout();
"This {0} is an {1}".Cout("sentence", "example");

it's concise and kindof funky.

anthonybell
  • 5,790
  • 7
  • 42
  • 60
  • 1
    Why are creating a method that essentially does nothing but swap the order of some arguments around? – mpen Aug 26 '13 at 02:56
  • 2
    So you don't have to type `Console.WriteLine(...)` (which is pretty long to have to type frequently). You could write your own method to do this but using a string extension is a little more elegant IMHO. – anthonybell Aug 26 '13 at 03:34
  • 10
    It's not long if you use tab-completion. You're creating two ways of doing the exact same thing, which confuses other developers. Further, it's not more elegant; you might see this sort of thing in Python, but it's weird in C#. – mpen Aug 26 '13 at 03:39
  • 4
    +1 for making an extension method. Although... since C# isn't C++, I might personally call it something like .ToConsole() instead of Cout(). Granted, the "C" in "Cout" means console, and .ToConsole() is longer, but .ToConsole() is also a more common general .NET pattern and probably makes more sense to somebody not coming from a C++ background, *and* Intellisense will pick it up and let you just type .ToC and hit the spacebar to complete it, anyway. – Craig Tullis Apr 16 '14 at 18:29
  • 4
    -1: Not only is this a very specific use-case that cannot be easily applied generally, but it's also not even a case where Preprocessor Macros are warranted. – marknuzz May 28 '14 at 06:12
  • 7
    This is not a preprocessor macro. The solution to this question clearly states: "C# does not support preprocessor macros like c" – anthonybell May 28 '14 at 20:39
  • I'd prefer a shortcut lambda instead: ``Action log = Console.WriteLine;`` – Binkan Salaryman May 13 '15 at 14:33
  • `log("The only problem is {0} doesn't {1}", "this", "work");` – anthonybell May 13 '15 at 19:55
  • 1
    Extension methods don't survive reflection. – Aetherus Aug 10 '16 at 10:14
  • 1
    This is an awesome solution to the log message conundrum. When writing bulletproof code it is often littered with failure mode detection, which results in repetitive prefix gak which I don't care about. It makes for cleaner code to turn the prefix gak into a suffix which I can avoid reading. – Keith Knauber Nov 04 '16 at 17:12
14

While you can't write macros, when it comes to simplifying things like your example, C# 6.0 now offers static usings. Here's the example Martin Pernica gave on his Medium article:

using static System.Console; // Note the static keyword

namespace CoolCSharp6Features
{
  public class Program
  {
    public static int Main(string[] args)
    {
      WriteLine("Hellow World without Console class name prefix!");

      return 0;
    }
  }
}
sraboy
  • 903
  • 2
  • 13
  • 26
9

There is no direct equivalent to C-style macros in C#, but inlined static methods - with or without #if/#elseif/#else pragmas - is the closest you can get:

        /// <summary>
        /// Prints a message when in debug mode
        /// </summary>
        [MethodImpl(MethodImplOptions.AggressiveInlining)]
        public static unsafe void Log(object message) {
#if DEBUG
            Console.WriteLine(message);
#endif
        }

        /// <summary>
        /// Prints a formatted message when in debug mode
        /// </summary>
        /// <param name="format">A composite format string</param>
        /// <param name="args">An array of objects to write using format</param>
        [MethodImpl(MethodImplOptions.AggressiveInlining)]
        public static unsafe void Log(string format, params object[] args) {
#if DEBUG
            Console.WriteLine(format, args);
#endif
        }

        /// <summary>
        /// Computes the square of a number
        /// </summary>
        /// <param name="x">The value</param>
        /// <returns>x * x</returns>
        [MethodImpl(MethodImplOptions.AggressiveInlining)]
        public static double Square(double x) {
            return x * x;
        }

        /// <summary>
        /// Wipes a region of memory
        /// </summary>
        /// <param name="buffer">The buffer</param>
        [MethodImpl(MethodImplOptions.AggressiveInlining)]
        public static unsafe void ClearBuffer(ref byte[] buffer) {
            ClearBuffer(ref buffer, 0, buffer.Length);
        }

        /// <summary>
        /// Wipes a region of memory
        /// </summary>
        /// <param name="buffer">The buffer</param>
        /// <param name="offset">Start index</param>
        /// <param name="length">Number of bytes to clear</param>
        [MethodImpl(MethodImplOptions.AggressiveInlining)]
        public static unsafe void ClearBuffer(ref byte[] buffer, int offset, int length) {
            fixed(byte* ptrBuffer = &buffer[offset]) {
                for(int i = 0; i < length; ++i) {
                    *(ptrBuffer + i) = 0;
                }
            }
        }

This works perfectly as a macro, but comes with a little drawback: Methods marked as inlined will be copied to the reflection part of your assembly like any other "normal" method.

Binkan Salaryman
  • 3,008
  • 1
  • 17
  • 29
3

Luckily, C# has no C/C++-style preprocessor - only conditional compilation and pragmas (and possibly something else I cannot recall) are supported. Unfortunatelly, C# has no metaprogramming capabilities (this may actually relate to your question to some extent).

Anton Gogolev
  • 113,561
  • 39
  • 200
  • 288
  • 8
    I would say it's not the job of the programming language to enforce good style. C# has goto's and a developer has the intelligence not to use them. The same applies for macros, but sometimes I would really like to have them! – bytecode77 Jun 30 '16 at 10:17
  • Example: "Dispatcher.Invoke(delegate" would be nice to have as a macro in your WPF code. – bytecode77 Aug 03 '16 at 07:54
  • @bytecode77 Gotos are useful in C#, C and C++ to jump out of nested loops. An approach like Java's continue/break to label is more intelligent but gotos are fine for that. However, macros are not useful in a language where multiplatform support is handled by the language. – Winter Jul 10 '17 at 12:46
  • 1
    Even though I encourage both pro's *and* cons for anything - I don't see how goto'ing out of a loop is superior over a boolean `quit` variable. Once nested statements come into place, code gets hard to read and maintain. PHP has the `break 2..n;` statement, C# doesn't. However, C# has enough LINQ extensions to actually not use nested loops in many cases, making the code readable in a different way - which I honestly prefer. – bytecode77 Jul 10 '17 at 13:08
  • We got here discussing how to deal with hand-written profiling of a hot loop. Luckily is a really bad choice of word. – Joshua Jul 16 '18 at 19:33
  • No Macros! Wow! What if I want to do a simple conversion and assign it to a variable at the time the variable is defined, e.g. #define CONVERT_AMPS_TO_ADC(AMPS) (Uint16)(AMPS*(6.0/2047.0)/0.1). Uint16 u16CurrentLimit_ADC = CONVERT_AMPS_TO_ADC(62.5). Isn't that much easier to understand than Uint16 u16CurrentLimit_ADC = (Uint16)(62.2*(6.0/2047.0)/0.1). This may have to be done many places in the code. There are too many examples to list them here. Macros can make the code more simple, more readable and help protect against typos.. – user1164199 Aug 26 '20 at 16:21
1

I would suggest you to write extension, something like below.

public static class WriteToConsoleExtension
{
   // Extension to all types
   public static void WriteToConsole(this object instance, 
                                     string format, 
                                     params object[] data)
   {
       Console.WriteLine(format, data);
   }
}

class Program
{
    static void Main(string[] args)
    {
        Program p = new Program();
        // Usage of extension
        p.WriteToConsole("Test {0}, {1}", DateTime.Now, 1);
    }
}

Hope this helps (and not too late :) )

Milan Jaric
  • 5,556
  • 2
  • 26
  • 34
  • 5
    for me this is just confusing – LuckyLikey Dec 14 '15 at 10:56
  • 3
    *Extensionitis*, as I sometimes call it. Building an API DLL for me often lead to more and more extensions rather than methods. E.g. `ValidateUrl(this string)` - Something which by now I prefer in a class, as this both tends to bloat intellisense (especially with `this object`) and makes finding such methods obscure sometimes. Having `Validate.Url(string)` doesn't blow up the code and is evidently easy for others to find and utilize. – bytecode77 Jul 10 '17 at 13:13
  • I do agree with you – Milan Jaric Jul 11 '17 at 13:49
1

Turn the C Macro into a C# static method in a class.

Robert Deml
  • 12,390
  • 20
  • 65
  • 92
  • 8
    This doesn’t get you CPP macro stringification support. Macros are useful because they treat the code more like plain text than code. However, the OP doesn’t seem to actually want/need macros. For his purposes, this would be a perfectly valid (and better) solution. – binki Feb 11 '14 at 22:14
0

Use lambdas

void print(string x) => Trace.WriteLine(x);
void println(string x) => Console.WriteLine(x);
void start(string x) => Process.Start(x);

void done() => Trace.WriteLine("Done");
void hey() => Console.WriteLine("hey");
omerbguclu
  • 69
  • 6