views:

201

answers:

5

(See below solution I created using the answer I accepted)

I'm trying to improve the maintainability of some code involving reflection. The app has a .NET Remoting interface exposing (among other things) a method called Execute for accessing parts of the app not included in its published remote interface.

Here is how the app designates properties (a static one in this example) which are meant to be accessible via Execute:

RemoteMgr.ExposeProperty("SomeSecret", typeof(SomeClass), "SomeProperty");

So a remote user could call:

string response = remoteObject.Execute("SomeSecret");

and the app would use reflection to find SomeClass.SomeProperty and return its value as a string.

Unfortunately, if someone renames SomeProperty and forgets to change the 3rd parm of ExposeProperty(), it breaks this mechanism.

I need to the equivalent of:

SomeClass.SomeProperty.GetTheNameOfThisPropertyAsAString()

to use as the 3rd parm in ExposeProperty so refactoring tools would take care of renames.

Is there a way to do this? Thanks in advance.

Okay, here's what I ended up creating (based upon the answer I selected and the question he referenced):

// <summary>
// Get the name of a static or instance property from a property access lambda.
// </summary>
// <typeparam name="T">Type of the property</typeparam>
// <param name="propertyLambda">lambda expression of the form: '() => Class.Property' or '() => object.Property'</param>
// <returns>The name of the property</returns>
public string GetPropertyName<T>(Expression<Func<T>> propertyLambda)
{
    var me = propertyLambda.Body as MemberExpression;

    if (me == null)
    {
        throw new ArgumentException("You must pass a lambda of the form: '() => Class.Property' or '() => object.Property'");
    }

    return me.Member.Name;
 }

Usage:

// Static Property
string name = GetPropertyName(() => SomeClass.SomeProperty);

// Instance Property
string name = GetPropertyName(() => someObject.SomeProperty);

Now with this cool capability, it's time to simplify the ExposeProperty method. Polishing doorknobs is dangerous work...

Thanks everyone.

+4  A: 

You can use Reflection to obtain the actual names of the properties.

http://www.csharp-examples.net/reflection-property-names/

If you need a way to assign a "String Name" to a property, why don't you write an attribute that you can reflect over to get the string name?

[StringName("MyStringName")]
private string MyProperty
{
    get { ... }
}
Robert Harvey
Ya, that's how the app handles incoming requests for "SomeSecret", but it doesn't give me a tool for the ExposeProperty problem.
Jim C
@Jim: See my edit
Robert Harvey
Interesting...then you could rename MyProperty to your hearts content as long as you don't mess with MyStringName, and if for some reason you do want to change it then you need to modify the ExposeProperty parm. At least I could add a comment next to the attribute with such a warning since you have to be looking at it to change the attribute's value (unlike renaming a property, which can be done from any reference location).
Jim C
A: 

You can use the StackTrace class to get the name of the current function, (or if you put the code in a function, then step down a level and get the calling function).

See http://msdn.microsoft.com/en-us/library/system.diagnostics.stacktrace(VS.71).aspx

Sprotty
I don't know where you had in mind to capture the stack trace, but I can't think of one that would contain the name of the property.
Jim C
You can do this, but this can lead to unexpected results (including exceptions) due to compiler inlining optimizations. http://www.smelser.net/blog/post/2008/11/27/Be-carefull-with-that-Stack-Trace.aspx
JoeGeeky
+2  A: 

There's a well-known hack to extract it from lambda expression (this is from the PropertyObserver class, by Josh Smith, in his MVVM foundation):

    private static string GetPropertyName
        (Expression<Func<TPropertySource, object>> expression)
    {
        var lambda = expression as LambdaExpression;
        MemberExpression memberExpression;
        if (lambda.Body is UnaryExpression)
        {
            var unaryExpression = lambda.Body as UnaryExpression;
            memberExpression = unaryExpression.Operand as MemberExpression;
        }
        else
        {
            memberExpression = lambda.Body as MemberExpression;
        }

        Debug.Assert(memberExpression != null, 
           "Please provide a lambda expression like 'n => n.PropertyName'");

        if (memberExpression != null)
        {
            var propertyInfo = memberExpression.Member as PropertyInfo;

            return propertyInfo.Name;
        }

        return null;
    }

Sorry, this was missing some context. This was part of a larger class where TPropertySource is the class containing the property. You could make the function generic in TPropertySource to extract it from the class. I recommend taking a look at the full code from the MVVM Foundation.

Dan Bryant
With an example of how to call the function, this is certainly a +1. Oops, didn't see that there is one in the debug assertion - that's why making a developer horizontally scroll to get to the important portion of a line is evil ;)
OregonGhost
Hmmm...I need to dissect this one to understand it.
Jim C
Visual Studio 2008 flags "TPropertySource" as error ("cannot be found").
Jim C
I just realized its a type name not just a symbol <T> as in C++. What does TPropertySource represent?
Jim C
A: 

The PropertyInfo class should help you achieve this, if I understand correctly.

  1. Type.GetProperties() method

    PropertyInfo[] propInfos = typeof(ReflectedType).GetProperties();
    propInfos.ToList().ForEach(p => 
        Console.WriteLine(string.Format("Property name: {0}", p.Name));
    

Is this what you need?

Will Marcouiller
Nope, although I do use GetProperties when the app receives the request for "SomeSecret". The app looks up "SomeSecret" in a map to discover it needs to find a property called "SomeProperty" in a class called "SomeClass".
Jim C
+3  A: 

Using GetMemberInfo from here: http://stackoverflow.com/questions/671968/retrieving-property-name-from-lambda-expression you can do something like this:

RemoteMgr.ExposeProperty(() => SomeClass.SomeProperty)

public class SomeClass
{
    public static string SomeProperty
    {
        get { return "Foo"; }
    }
}

public class RemoteMgr
{
    public static void ExposeProperty<T>(Expression<Func<T>> property)
    {
        var expression = GetMemberInfo(property);
        string path = string.Concat(expression.Member.DeclaringType.FullName,
            ".", expression.Member.Name);
        // Do ExposeProperty work here...
    }
}

public class Program
{
    public static void Main()
    {
        RemoteMgr.ExposeProperty("SomeSecret", () => SomeClass.SomeProperty);
    }
}
Daniel Renshaw
That's totally cool. Looks like it would work on any property type, too.
Jim C
I just tried it with both instance and static properties. So far so good.
Jim C