5

I often want to do this:

public void Foo(Bar arg)
{
  throw new ArgumentException("Argument is incompatible with " + name(Foo));
}

Because if I change the name of Foo the IDE will refactor my error message too, what won't happen if I put the name of the method (or any other kind of member identifier) inside a string literal. The only way I know of implementing "name" is by using reflection, but I think the performance loss outweighs the mantainability gain and it won't cover all kinds of identifiers.

The value of the expression between parenthesis could be computed at compile time (like typeof) and optimized to become one string literal by changing the language specification. Do you think this is a worthy feature?

PS: The first example made it look like the question is related only to exceptions, but it is not. Think of every situation you may want to reference a type member identifier. You'll have to do it through a string literal, right?

Another example:

[RuntimeAcessibleDocumentation(Description="The class " + name(Baz) +
  " does its job. See method " + name(DoItsJob) + " for more info.")]
public class Baz
{
  [RuntimeAcessibleDocumentation(Description="This method will just pretend " +
    "doing its job if the argument " + name(DoItsJob.Arguments.justPretend) +
    " is true.")]
  public void DoItsJob(bool justPretend) 
  {
    if (justPretend)
      Logger.log(name(justPretend) + "was true. Nothing done.");
  }
}

UPDATE: this question was posted before C# 6, but may still be relevant for those who are using previous versions of the language. If you are using C# 6 check out the nameof operator, which does pretty much the same thing as the name operator in the examples above.

ygormutti
  • 358
  • 3
  • 17
  • 3
    Why would you like to do that? `Foo` will be on stack trace anyway. – MarcinJuraszek Aug 08 '13 at 07:06
  • 5
    _"The only way I know of implementing "name" is by using reflection, but I think the performance loss outweighs the mantainability gain"_ This argument shouldn't matter since your exception should be raised rarely. – Tim Schmelter Aug 08 '13 at 07:07
  • @MarcinJuraszek think of other situations where you may want to reference a member by its name (e.g. documentation strings inside attributes/xmldoc, reflection, etc) – ygormutti Aug 08 '13 at 10:42
  • @TimSchmelter it matters because this would limit the use cases of "name" to code executed rarely – ygormutti Aug 08 '13 at 10:44
  • 1
    I do some things like this in a little C# pre-processor program I run as part of every build, and (if necessary) modifies the C# source file. For example copying the name of a variable into a string assignment statement. It's messy but doable. If it is of any interest I'll write up more details in an answer. – RenniePet Aug 09 '13 at 01:49
  • @RenniePet Please, do it! A pre-processor is a very interesting solution! Even if it is not the official pre-processor for the language (then nothing guarantees that the code doesn't already use your pre-processor macro names) you could use weird identifier names, like `@nameof`, allowing VS to refactor the code and avoiding name clashes. – ygormutti Aug 09 '13 at 18:35

5 Answers5

11

well, you could cheat and use something like:

public static string CallerName([CallerMemberName]string callerName = null)
{
    return callerName;
}

and:

public void Foo(Bar arg)
{
  throw new ArgumentException("Argument is incompatible with " + CallerName());
}

Here, all the work is done by the compiler (at compile-time), so if you rename the method it will immediately return the correct thing.

Marc Gravell
  • 1,026,079
  • 266
  • 2,566
  • 2,900
  • Better than reflection but he wants a compile-time construct... seems like something PostSharp could do. – It'sNotALie. Aug 08 '13 at 07:07
  • 4
    @It'sNotALie actually, that **is** a compile-time construct; at compile-time, that becomes: `... + CallerName("Foo")` - there is **nothing** happening at runtime here, except a `return callerName`, which should be inlined - so there won't even be a static-call once it has been JITted – Marc Gravell Aug 08 '13 at 07:08
  • I didn't think about that: +1 to you. – It'sNotALie. Aug 08 '13 at 07:10
  • +1 - CallerName is in the CompilerServices namespace. It tags the parameter for the compiler to insert the name of the method. Very cool but only in .net 4.5+ – Gusdor Aug 08 '13 at 07:11
  • 2
    @Gusdor actually, since it is a compiler feature, it will be available in any framework version, with the caveat that you might need to *create the attribute in the correct namespace yourself*. – Marc Gravell Aug 08 '13 at 07:12
  • And what version of C#/.Net do I need to use `CallerMemberName`? – Gabe Aug 08 '13 at 07:13
  • @Gabe .NET 4.5 unless you recreate the attribute in the correct namespace – It'sNotALie. Aug 08 '13 at 07:16
  • @MarcGravell Is that confirmed? It depends if the attribute is referenced by the compiler using a strong name. It also depends on the toolset you are using. The .net 3.0 compiler may not do this. – Gusdor Aug 08 '13 at 07:16
  • 2
    @Gusdor I can check that in a few minutes, but: I would *expect* it to work, since other similar attribute-based features (extension methods, etc) have always worked fine this way. – Marc Gravell Aug 08 '13 at 07:17
  • 1
    @Gusdor yes, tested successfully on .NET 2.0 with C# 5 – Marc Gravell Aug 08 '13 at 07:19
  • @MarcGravell this is a brilliant hack! Unfortunately it doesn't solve the problem I wanted to expose, because it can only get the name of the caller (I can't get Foo's name from other method) and only works for callables. What about parameters and fields? :) – ygormutti Aug 08 '13 at 10:55
  • @Gusdor yes, you need C# 5; VS2010 does not use C# 5 – Marc Gravell Aug 08 '13 at 12:51
  • @ygormutti indeed, it doesn't work for parameters and fields; alas, there is no `nameof` operator; there *are* some tricks you can do with expression-trees, but personally I'd just hard-code the string – Marc Gravell Aug 08 '13 at 12:52
  • @MarcGravell the downside of a hardcoded string is that it is no longer supported by refactoring. If someone changes the name of the property, your literal is now useless and you get a hidden error. If it was a symbol (like with expression trees) you would get a compiler error. – Gusdor Aug 08 '13 at 13:04
  • @MarcGravell I know. Unfortunately there is no `nameof` operator in C#, so I think I will accept the answer that has the best compromise between the number of supported identifier kinds, simplicity and performance. IMHO, this feature is worthwhile implementing. – ygormutti Aug 08 '13 at 13:22
  • 1
    Resharper does handle renaming when the names are used in hardcoded strings in some cases (as well as recognising bindings in XAML, etc). It won't make the hardcoded string solution perfect, but it can help. – Niall Connaughton Aug 09 '13 at 01:31
  • 1
    To show a example of what Marc is talking about, in [one of my older answers](http://stackoverflow.com/questions/11346554/can-i-use-extension-methods-and-linq-in-net-2-0-or-3-0/11346555#11346555) I show how to use extension methods in .NET 2.0 or 3.0 by using at least Visual Studio 2008 + creating the attribute in the correct namespace. – Scott Chamberlain Aug 09 '13 at 07:10
6

If you simply want the current method name: MethodBase.GetCurrentMethod().Name

If it's a type typeof(Foo).Name

If you want the name of a variable/parameter/field/property, with a little Expression tree

public static string GetFieldName<T>(Expression<Func<T>> exp)
{
    var body = exp.Body as MemberExpression;

    if (body == null)
    {
        throw new ArgumentException();
    }

    return body.Member.Name;
}

string str = "Hello World";
string variableName = GetFieldName(() => str);

For method names it's a little more tricky:

public static readonly MethodInfo CreateDelegate = typeof(Delegate).GetMethod("CreateDelegate", BindingFlags.Static | BindingFlags.Public, null, new[] { typeof(Type), typeof(object), typeof(MethodInfo) }, null);

public static string GetMethodName<T>(Expression<Func<T>> exp)
{
    var body = exp.Body as UnaryExpression;

    if (body == null || body.NodeType != ExpressionType.Convert)
    {
        throw new ArgumentException();
    }

    var call = body.Operand as MethodCallExpression;

    if (call == null)
    {
        throw new ArgumentException();
    }

    if (call.Method != CreateDelegate)
    {
        throw new ArgumentException();
    }

    var method = call.Arguments[2] as ConstantExpression;

    if (method == null)
    {
        throw new ArgumentException();
    }

    MethodInfo method2 = (MethodInfo)method.Value;

    return method2.Name;
}

and when you call them you have to specify the type of a compatible delegate (Action, Action<...>, Func<...> ...)

string str5 = GetMethodName<Action>(() => Main);
string str6 = GetMethodName<Func<int>>(() => Method1);
string str7 = GetMethodName<Func<int, int>>(() => Method2);

or more simply, without using expressions :-)

public static string GetMethodName(Delegate del)
{
    return del.Method.Name;
}

string str8 = GetMethodName((Action)Main);
string str9 = GetMethodName((Func<int>)Method1);
string str10 = GetMethodName((Func<int, int>)Method2);
xanatos
  • 109,618
  • 12
  • 197
  • 280
1

As has been covered, using this approach for exceptions seems unnecessary due to the method name being in the call stack on the exception.

In relation to the other example in the question of logging the parameter value, it seems PostSharp would be a good candidate here, and probably would allow lots of new features of this kind that you're interested in.

Have a look at this page on PostSharp which came up when I searched for how to use PostSharp to log parameter values (which it covers). An excerpt taken from that page:

You can get a lot of useful information with an aspect, but there are three popular categories:

  • Code information: function name, class name, parameter values, etc. This can help you to reduce guessing in pinning down logic flaws or edge-case scenarios
  • Performance information: keep track of how much time a method is taking
  • Exceptions: catch select/all exceptions and log information about them
Niall Connaughton
  • 15,518
  • 10
  • 52
  • 48
  • 2
    Sure, your logic is fine for error messages, but what about log (trace) messages? You don't want to have to do a stack trace in every function that has tracing, and you certainly don't want to have the names of your methods hardcoded everywhere. – Gabe Aug 08 '13 at 07:52
  • This example is specifically talking about exceptions, which are both exceptional (unusual) and also expensive to throw in the first place. If you want to just trace that methods are getting hit, and do it quickly, I would either use Mark Gravell's CallerMemberName solution or some auto-weaved code generation like PostSharp as It'sNotALie suggested. – Niall Connaughton Aug 08 '13 at 08:21
  • 1
    The question is how to reference a member name without a literal; the use for exceptions is just an example. Saying that it's a bad example doesn't invalidate the question, it just means you need a better example. – Gabe Aug 08 '13 at 08:26
  • In terms of logging entry into a method, performance timings, etc, PostSharp is the way to go. The updated question uses an Attribute - this is heading in the direction of PostSharp. You could use this approach to log the name of the method without needing to make any reference to the method in the attribute. – Niall Connaughton Aug 09 '13 at 01:18
  • In terms of grabbing the name of a local variable, I can't think of a way to do it, and I've never really felt the need. If you're logging some value that's been calculated I'd log the meaning of the data without the name, eg "User's age calculated to be 34", not "userAge calculated to be 34". – Niall Connaughton Aug 09 '13 at 01:21
1

The original question is named "How to refer to an identifier without writing it into a string literal in C#?" This answer does not answer that question, instead, it answers the question "How to refer to an identifier by writing its name into a string literal using a preprocessor?"

Here is a very simple "proof of concept" C# preprocessor program:

using System;
using System.IO;

namespace StackOverflowPreprocessor
{
   /// <summary>
   /// This is a C# preprocessor program to demonstrate how you can use a preprocessor to modify the 
   /// C# source code in a program so it gets self-referential strings placed in it.
   /// </summary>
   public class PreprocessorProgram
   {
      /// <summary>
      /// The Main() method is where it all starts, of course. 
      /// </summary>
      /// <param name="args">must be one argument, the full name of the .csproj file</param>
      /// <returns>0 = OK, 1 = error (error message has been written to console)</returns>
      static int Main(string[] args)
      {
         try
         {
            // Check the argument
            if (args.Length != 1)
            {
               DisplayError("There must be exactly one argument.");
               return 1;
            }

            // Check the .csproj file exists
            if (!File.Exists(args[0]))
            {
               DisplayError("File '" + args[0] + "' does not exist.");
               return 1;
            }

            // Loop to process each C# source file in same folder as .csproj file. Alternative 
            //  technique (used in my real preprocessor program) is to read the .csproj file as an 
            //  XML document and process the <Compile> elements.
            DirectoryInfo directoryInfo = new DirectoryInfo(Path.GetDirectoryName(args[0]));
            foreach (FileInfo fileInfo in directoryInfo.GetFiles("*.cs"))
            {
               if (!ProcessOneFile(fileInfo.FullName))
                  return 1;
            }
         }
         catch (Exception e)
         {
            DisplayError("Exception while processing .csproj file '" + args[0] + "'.", e);
            return 1;
         }

         Console.WriteLine("Preprocessor normal completion.");
         return 0; // All OK
      }


      /// <summary>
      /// Method to do very simple preprocessing of a single C# source file. This is just "proof of 
      /// concept" - in my real preprocessor program I use regex and test for many different things 
      /// that I recognize and process in one way or another.
      /// </summary>
      private static bool ProcessOneFile(string fileName)
      {
         bool fileModified = false;
         string lastMethodName = "*unknown*";
         int i = -1, j = -1;

         try
         {
            string[] sourceLines = File.ReadAllLines(fileName);
            for (int lineNumber = 0; lineNumber < sourceLines.Length - 1; lineNumber++)
            {
               string sourceLine = sourceLines[lineNumber];

               if (sourceLine.Trim() == "//?GrabMethodName")
               {
                  string nextLine = sourceLines[++lineNumber];
                  j = nextLine.IndexOf('(');
                  if (j != -1)
                     i = nextLine.LastIndexOf(' ', j);
                  if (j != -1 && i != -1 && i < j)
                     lastMethodName = nextLine.Substring(i + 1, j - i - 1);
                  else
                  {
                     DisplayError("Unable to find method name in line " + (lineNumber + 1) + 
                                  " of file '" + fileName + "'.");
                     return false;
                  }
               }

               else if (sourceLine.Trim() == "//?DumpNameInStringAssignment")
               {
                  string nextLine = sourceLines[++lineNumber];
                  i = nextLine.IndexOf('\"');
                  if (i != -1 && i != nextLine.Length - 1)
                  {
                     j = nextLine.LastIndexOf('\"');
                     if (i != j)
                     {
                        sourceLines[lineNumber] = 
                                    nextLine.Remove(i + 1) + lastMethodName + nextLine.Substring(j);
                        fileModified = true;
                     }
                  }
               }
            }

            if (fileModified)
               File.WriteAllLines(fileName, sourceLines);
         }
         catch (Exception e)
         {
            DisplayError("Exception while processing C# file '" + fileName + "'.", e);
            return false;
         }

         return true;
      }


      /// <summary>
      /// Method to display an error message on the console. 
      /// </summary>
      private static void DisplayError(string errorText)
      {
         Console.WriteLine("Preprocessor: " + errorText);
      }


      /// <summary>
      /// Method to display an error message on the console. 
      /// </summary>
      internal static void DisplayError(string errorText, Exception exceptionObject)
      {
         Console.WriteLine("Preprocessor: " + errorText + " - " + exceptionObject.Message);
      }
   }
}

And here's a test file, based on the first half of the original question:

using System;

namespace StackOverflowDemo
{
   public class DemoProgram
   {
      public class Bar
      {}


      static void Main(string[] args)
      {}


      //?GrabMethodName
      public void Foo(Bar arg)
      {
         //?DumpNameInStringAssignment
         string methodName = "??";  // Will be changed as necessary by preprocessor

         throw new ArgumentException("Argument is incompatible with " + methodName);
      }
   }
}

To make the running of the preprocessor program a part of the build process you modify the .csproj file in two places. Insert this line in the first section:

<UseHostCompilerIfAvailable>false</UseHostCompilerIfAvailable>

(This is optional - see here https://stackoverflow.com/a/12163384/253938 for more information.)

And at the end of the .csproj file replace some lines that are commented-out with these lines:

  <Target Name="BeforeBuild">
    <Exec WorkingDirectory="D:\Merlinia\Trunk-Debug\Common\Build Tools\Merlinia Preprocessor\VS2012 projects\StackOverflowPreprocessor\bin" Command="StackOverflowPreprocessor.exe &quot;$(MSBuildProjectFullPath)&quot;" />
  </Target>

Now when you recompile the test program the line that says

     string methodName = "??";  // Will be changed as necessary by preprocessor

will be magically converted to say

     string methodName = "Foo";  // Will be changed as necessary by preprocessor

OK?

Community
  • 1
  • 1
RenniePet
  • 11,420
  • 7
  • 80
  • 106
  • This approach of getting names is very limited because you can grab only names declared in the same file and dump them only before the next grabbing. Also, I think this syntax doesn't look C#-esque enough. The language developers would probably use a syntax similar to typeof. A preprocessor syntax using the symbol table generated by the compiler would be an almost perfect solution. The perfect solution (IMHO) would be to have this feature in the language itself. Because a preprocessor is the best solution we can have today, I'm marking your answer as accepted. – ygormutti Aug 27 '13 at 18:11
  • @ygormutti: "This approach of getting names is very limited ..." Well, that dependes to some extent on how complicated you make your preprocessor. In the preprocessor that I actually use I place the strings containing the names in an extra C# source file and mark them "public" and "static" so they can be accessed from anywhere in the program. In theory you could write them to a database or whatever you want. – RenniePet Aug 27 '13 at 22:15
  • @ygormutti: "I think this syntax doesn't look C#-esque enough." I agree, but this was a simple "proof of concept" bit of code. The preprocessor that I actually use uses "fake" attributes such as "[GenerateNameConstant]". This involves actually defining the attributes and including them in your program, even though they are never used as attributes, only as something that the preprocessor recognizes. And my real preprocessor uses regex to recognize the various things that I search for and act upon. Hope these suggestions help. – RenniePet Aug 27 '13 at 22:20
  • @ygormutti: You might also want to look into creating a Visual Studio extension. I've never tried it myself, but there are MSDN resources about how to do it. I don't think that would work with off-line builds using MSBuild, but maybe that doesn't matter. – RenniePet Aug 27 '13 at 22:27
0

Version 6 of C# has introduced the nameof operator which works like the name operator described in the examples of the question, but with some restrictions. Here are some examples and excerpts from the C# FAQ blog:

(if x == null) throw new ArgumentNullException(nameof(x));

You can put more elaborate dotted names in a nameof expression, but that’s just to tell the compiler where to look: only the final identifier will be used:

WriteLine(nameof(person.Address.ZipCode)); // prints "ZipCode"

Note: there are small design changes to nameof since the Preview was built. In the preview, dotted expressions like in the last example, where person is a variable in scope, are not allowed. Instead you have to dot in through the type.

ygormutti
  • 358
  • 3
  • 17