Print

Dear C# Language Designers

Have you ever wondered why the C# language doesn't have a specific feature, or why it works the way it does? As a matter of fact, I did, and I mailed them over a year ago. I never got an answer though. Now I'm publishing this mail, hoping to start a good discussion.

It's already over a year ago that I sent this e-mail to 'ask a language designer'. I forgot all about it and never got an answer. And now a ask you, my dear readers, what do you think of this feature request?

Dear C# Language Designers,
 
The last several years, refactoring and data binding have become more and more popular. Refactoring enables us to change a function name throughout an enormous code base with almost a single click, while data binding allows us to bind to objects and discover properties and methods at runtime.
 
These two features, however, don't match. In the example below, I will point out this problem, after which I’ll describe the C# feature I’d really like to have.
 
In ASP.NET 2.0 we have a new control called ObjectDataSource. This control lets us bind to a method of a certain type, by defining the TypeName and SelectMethod. The problem here, is that these properties are strings. The effect of which is that a type name or method name can not be found by the refactoring tool, like in the following code:
class MyClass
{
public void MyMethod() { }
public void MyMethod(int i, double d) { }
public void MyProperty { get; set; }
}

static void Main()
{
ObjectDataSource ds = new ObjectDataSource();
s.TypeName = "MyAssembly.MyClass";
ds.SelectMethod = "MyMethod";
}
We can rewrite the previous code snippet in such a way that the strings will disappear and the code will become actually refactorable.
class MyClass [...]

delegate void MyDelegate();
 
static void Main()
{
ObjectDataSource ds = new ObjectDataSource();
ds.TypeName = typeof(MyClass).FullName;
ds.SelectMethod =
(new MyDelegate((new MyClass()).MyMethod)).Method.Name;
}
Right away we can see the problem. For setting the TypeName, the problem was easily solved by using typeof(MyClass).FullName. Retrieving the name of the method is far more complex. Besides that, for every method for which we want to extract it’s name, we must define a delegate with the same contract as the method. Even worse, retrieving property names is impossible (without reintroducing strings of course).
 
What we need is a feature, that brings the two worlds together, so we can build more maintainable programs. A construct, much like the typeof operator, that can determine the name of a function at compile time! I can imagine the code to look like this:
ObjectDataSource ds = new ObjectDataSource();
ds.TypeName = typeof(MyClass).FullName;
ds.SelectMethod = nameof(MyClass.MyMethod(int, double));
string propertyName = nameof(MyClass.MyProperty);
As you can see I propose a special language syntax, that can only hold within the brackets of the nameof operator. To be able to uniquely identify a method (overload), the nameof operator must contain the function's argument types. Note that member and static functions are both written with the static method syntax.
 
I’d really like to hear your thoughts on this matter.
 
Best regards
 
 
Steven

Update 2008-12-03:
Eric Lippert posted a great comment (already over 3 months ago). I have been playing around with his code sample and extended it a bit, but forgotten all about it. Today a former colleague of mine asked about this code, so here is my version. Good luck with it:

using System;
using System.Collections.Generic;
using System.Linq;
using System.Linq.Expressions;

public static class Utilities
{
public static string NameOf(Expression<Action> x)
{
return NameOfInternal(x.Body);
}

public static string NameOf<T>(Expression<Func<T, object>> x)
{
return NameOfInternal(x.Body);
}

private static string NameOfInternal(Expression expression)
{
if (expression is UnaryExpression)
{
expression = ((UnaryExpression)expression).Operand;
}

if (expression is MemberExpression)
{
return BuildMemberExpressionName(expression);
}
else if (expression is MethodCallExpression)
{
return ((MethodCallExpression)expression).Method.Name;
}
else if (expression is ConstantExpression)
{
var value = ((ConstantExpression)expression).Value;

return value != null ? value.ToString() : null;
}
else
{
throw new InvalidOperationException(string.Format("{0} is not supported.", expression.GetType()));
}
}

private static string BuildMemberExpressionName(Expression expression)
{
var members = new List<string>();

var memberExpression = expression as MemberExpression;

while (memberExpression != null)
{
members.Add(memberExpression.Member.Name);

memberExpression = memberExpression.Expression as MemberExpression;
}

members.Reverse();

return string.Join(".", members.ToArray());
}
}

- C# - four comments / No trackbacks - §

The code samples on my weblog are colorized using javascript, but you disabled javascript (for my website) on your browser. If you're interested in viewing the posted code snippets in color, please enable javascript.

four comments:

would definitely be a nice to have
Eber Irigoyen (URL) - 13 07 08 - 19:27

Hi Steven,

This is a good idea, and an interesting one. Were we to do this, we would make it much, much more general than "nameof".

Essentially what you are asking for is a syntax for obtaining reflection information; we already have such a thing in the form of the "typeof" operator, but that only gives you an object which represents a type. What you want is an operator which gives an object which represents other entities which could be reflected over -- methods, formal parameters, fields, etc.

We have discussed this operator many times on the C# design team; it's a frequently requested feature. We call it the "infoof" operator (which can be pronounced "info of" or, my preference, "in foof" :-) ).

Like I said, this is a good idea. However, we have no shortage of good ideas. We have way, way more good ideas than we could possibly design, implement, test, document and educate users about. This particular idea was high on our list of priorities for future versions of C#, but not high enough to make it into the next version. Hopefully we'll get it in soon.

In the meanwhile, there are a number of ways to get what you want. For example, a cheap and easy way to get the name of a parameter is:

MethodInfo.GetCurrentMethod().GetParameters()[0].Name

But that doesn't solve the more general problem. To solve the more general problem you could have the compiler generate you an expression tree, and then extract the information from it:

using System;
using System.Linq.Expressions;

class Program
{
static void Main(string[] args)
{
Console.WriteLine(Utilities.NameOf(() => args));
}
}

static class Utilities
{
public static string NameOf(Expression x)
{
return ((MemberExpression)x.Body).Member.Name;
}
}

If you wanted a method info you could replace "args" with a call to a method, and then write code that extracted the method info from the expression tree. And so on.

Thanks for your idea and the usage case. Good usage cases are always appreciated.
Eric Lippert (URL) - 14 07 08 - 18:56

Your blog software seems to have eaten all of the tag-looking stuff in my code sample. That should be an Expression of a Func of T...
Eric Lippert (URL) - 14 07 08 - 18:57

Eric,

Thank you very much for comment. I appreciate it very much. I’m also glad to hear that this idea was pretty high on your vNext list.

Your example works pretty well. When extended just a little bit, it will also work for methods (and even methods that return void). The only elements that possibly can’t be ‘named of’ in this way are setter-only properties, but that shouldn’t be such a problem, in my opinion.

Thanks again for your input.

ps. I fixed your comment. The generic tags are now visible again.
Steven (URL) - 14 07 08 - 21:28


No trackbacks:

Trackback link:

Please enable javascript to generate a trackback url


  
Remember personal info?

/

Before sending a comment, you have to answer correctly a simple question everyone knows the answer to. This completely baffles automated spam bots.
 

  (Register your username / Log in)

Notify:
Hide email:

Small print: All html tags except <b> and <i> will be removed from your comment. You can make links by just typing the url or mail-address.