55

I was just hit with a minor issue in C#, it was just a copy-paste mistake but don't know how C# accept it.

This code gets compiled successfully...HOW

namespace DemoNS
{
    class DemoClass
    {
        String _ = new String('a', 1);        
    }
}

Is there any default significance of variable named _?

Bhargav Rao
  • 50,140
  • 28
  • 121
  • 140
Samy
  • 749
  • 1
  • 5
  • 8

5 Answers5

90

Nowadays with C# 7.0 the _ does have significance sometimes. It became the discard operator for the new out var feature. It is used when a function returns a value and you want to notify the compiler that you won't be using it - so it can be optimized out. Or when deconstructing (Another C# 7.0 feature) you can use it to ignore part of the tuple that you are not interested in.

Example out var

void Test(out int i) => i = 1;

Test(out _); // _ was never declared, it will still compile in C# 7.0

var r = _;   // error CS0103: The name '_' does not exist in the current context

Example deconstructing a Tuple

var Person = ("John", "Smith");

var (First, _) = Person; // '_' is not a declared

Debug.Print(First); // prints "John"
Debug.Print(_); // error CS0103: The name '_' does not exist in the current context

A problem arises if you do declare your own variable named _ and then use the discard operator it will cause ambiguity. This issue has been reported Here.

EDIT Above problem is not a problem as @maf-soft points out in comments. If _ was declared it is treated like a regular variable like it was pre C# 7.0.

EDIT 2021 A little overdue

In c# 8.0 _ also became the catch all operator in a switch expression, officially named the discard operator

Example discard operator

var moreThan20 = val switch
{
    >20 => "Yes",
    >50 => "Yes - way more!",
    _ => "No",
};

The discard operator assigns a value when no other pattern matches

MotKohn
  • 3,485
  • 1
  • 24
  • 41
  • 1
    I'd like to add that if you declare a variable named `_`, it is used as before, so old code won't break (this wasn't completely clear in your answer). That's ok for me, but maybe a warning would be good. – maf-soft May 05 '17 at 15:18
  • I'm not sure if that discard operator really belongs to the `out var` feature (yes, your source link gives that impression). – maf-soft May 05 '17 at 15:24
  • @maf-soft you are right. I am goint to comment on referenced gist. – MotKohn May 05 '17 at 15:26
  • Another interesting reference: http://stackoverflow.com/questions/42920622/c7-underscore-star-in-out-variable - and interestingly other ideas for the discard operator were using the `void` keyword :) – maf-soft May 05 '17 at 15:39
  • It's very unexpected. It's not like C#. – Bamdad Dec 23 '20 at 07:49
47

No, there is no default significance, _ is just a variable name like any other.

I like to use it in similar way to Prolog's anonymous variables: when you're creating a lambda that ignores one of its parameters, you can name it _:

EventHandler handler = (_, e) => Console.WriteLine(e);

On the other hand, I wouldn't use it anywhere else, you should use a descriptive name instead.

EDIT: Note that in C# 7.0, _ sometimes has special meaning. For example, you can write _ = new String('a', 1);, even if you didn't declare a variable named _.

svick
  • 236,525
  • 50
  • 385
  • 514
18

The previous answers were all useful, but I think they missed a use case. If you don't want to work with a function's return value, you can use the _ character, i.e.:

instead of

int returnvalue = RandomFunction();

you can do

_ = RandomFunction();
Olivér Raisz
  • 353
  • 3
  • 12
  • 7
    What's the benefit of `_ = RandomFunction();` over `RandomFunction();` (i.e. simply not assigning the return value at all)? – Marteng Mar 10 '22 at 11:15
  • 10
    Technically it does the same thing, but explicitly marking that you do not want to use the returned value 1) creates cleaner code and 2) tells the IDE that you did it intentionally, thus you won't get a warning for that. – Olivér Raisz Mar 13 '22 at 14:24
  • @OlivérRaisz Do you really think it creates cleaner code? If we have a "query" style function like this, which should not have side effects: `_ = GetUsers();` To me, this code is implying that there **are** side effects, why else would we discard the return value? On the other hand, if we have a "command": `_ = DeleteAllUsers();` In this case we just took the perfectly clean command call and made visually look like a query. In a lambda or to denote arguments that will go unused I think it works better, but for anyone reading, maybe don't sprinkle these all over the place... – Oscar Lundberg Dec 29 '22 at 15:19
  • @OscarLundberg: I don't see the logic in your comment. `DeleteAllUsers();` should be a `void` returning function; `_ = DeleteAllUsers();` should be a compiler error. If `GetUsers();` has no side-effects but returns a value, then it would be *pointless* to call it without using the return value. In both cases, the code you show would not be written. The *only* time there is a reason to use `_ = SomeFunction();` *is* if the function is being called for a side-effect, but its return value is not needed. `_ =` tells future programmers: "I am calling this for its side-effect". – ToolmakerSteve May 03 '23 at 01:16
  • @ToolmakerSteve I agree my comment was not very clear. The point I was trying to make is that having side effects **and** a return value does not make the code cleaner in my opinion, I think that a function should either have a return value or have side effects. The discussion was implying that this creates cleaner code but by my standards it indirectly creates less clean code since it enables you to have both – Oscar Lundberg May 04 '23 at 20:09
  • Thank you for clarifying. That is a useful principle where appropriate (as demonstrated by EIFFEL language in 1986) but IMHO is irrelevant to this discussion, which begins with the assumption that, for whatever reason, one has a function that causes side-effects and returns a value. For example, it is quite common for an action-performing function to return a success/fail bool or an error code. Usually one should handle that result, but (rarely) there are reasons not to. `_ =` does exactly what this answer says it does: makes it clear that programmer deliberately is ignoring the return value. – ToolmakerSteve May 04 '23 at 21:56
6

_ is a valid character the same as a or i and syntactically variables can start with _ so a single character name of _ is perfectly syntactically correct. Not a really good choice but will compile and work fine.

svick
  • 236,525
  • 50
  • 385
  • 514
Bueller
  • 2,336
  • 17
  • 11
  • this is not valid anymore with c# – VineetYadav May 29 '21 at 20:58
  • @VineetYadav. I don't see any update to c# that disallows this. Perhaps there is some compiler option? In [C# 7 language spec / Discards](https://learn.microsoft.com/en-us/dotnet/csharp/language-reference/language-specification/variables#9281-discards), there is this sentence: *"The example assumes that there is no declaration of the name _ in scope."*. [Implying that `_` is still a valid name. So that old code wouldn't break.] AFAIK, later c# specs simply added new features. Why would they invalidate existing code? – ToolmakerSteve May 03 '23 at 01:42
5

Its a discards, which is a placeholder variable and unused. It tells compiler that not interested in output value. Example: Created a File handing class. Constructor will initiate the process. And, we have Copy, Delete operation. Suppose, one particular class has the responsibility to initiate the process but no need to worry about other operations. Then I will declare like _ = new FileListener(); I will not worry about output. Other class can have instantiate FileListener obj = new FileListener(); or can call other operation as FileListener.CopyFile()

Sample:

    class Program
        {
            static void Main(string[] args)
            {
   /// Ignore the instance value but initialized the operation by instantiation
                _ = new FileListener();
                
                Console.WriteLine("Hello World!");
            }
        }
        
        public class FileListener 
        {
            public FileListener()
            {
                /// Logic to listen external file changes
            }
            public static void DeleteFile()
            { }
            public static void CopyFile()
            { }
        }