4

My class MyClass<t> has the following method

private static bool TypeHasProperty(string NameOfPropertyToMatch)
{
    var properties = typeof(t).GetProperties();
    var requiredProperty = properties.First(
            a => a.Name == NameOfPropertyToMatch);
    if (requiredProperty == null)
    {
        return false
    };

    return true;
}

This is called at the start of a static method:

MyClass<t>.SendToJavaScript(t InstanceOfType, string NameOfPropertyWithinType).

to make sure that InstanceOfType actually has a property of that name before continuing, because otherwise an exception wouldn't get thrown until waaaaay down the line (this information will eventually get serialized and sent to a Javascript Application, which needs to know which property to access in order to do it's job).

I would like for a good, type-safe way to call that method that will not cause the validation to fail if I decide to change the name of my properties later on down the line.

For instance, I do NOT want to call it like this:

MyClass<Person>.SendToJavaScript(MyPerson, "Name")

Because if I decide to change Name to LastName, in the Person class, I would have to make sure to come in and change that magic string.

What I WOULD want, is something like this:

MeClass<Person>.SendToJavaScript(
    MyPerson,
    TypeOf(Person).Properties.Name.ToString())

so that if I use Refactoring to change Name to LastName, the IDE will sweep through there and change my call as well.

Does anything like that exist? Or do I just need to be careful when I refactor? (I love me some F2)

BartoszKP
  • 34,786
  • 15
  • 102
  • 130
JHixson
  • 1,512
  • 2
  • 14
  • 29

2 Answers2

5

We use something like this in our code:

private static bool TypeHasProperty<P>(Expression<Func<T, P>> accessor)
{
    var propertyName = ((MemberExpression)accessor.Body).Member.Name;
    var properties = typeof(T).GetProperties();
    return properties.Any(a => a.Name == propertyName);
}

Use it:

class Foo
{
    public int Baz { get; set; }
}

MyClass<Foo>.TypeHasProperty(f => f.Baz);

Edit: Changed the query to use Any() as it expresses better what you actually want to do (does a property with the given name exist?)

Note that First() will throw if no match was found.

ChrisWue
  • 18,612
  • 4
  • 58
  • 83
  • Seems much better than my answer, I was too quick to stop and think ;) – BartoszKP Oct 03 '13 at 21:50
  • This is great. The super short answer for what I needed to realize was: Expressions. They are the best way to get type-safe access to an object's members. Nevertheless, I'm positive that my implementation is going to look very similar to this. – JHixson Oct 04 '13 at 00:29
  • With a call like this, is there even a need to verify that `T` actually has Member `P`? I seems impossible to legally call `TypeHasProperty` in such a way that it would fail. – JHixson Oct 04 '13 at 16:41
  • @JHixson: It could be a field rather than a property in which case you could still make the call but verification would fail. – ChrisWue Oct 04 '13 at 18:31
4

Try this:

MeClass<Person>.SendToJavaScript(MyPerson, x => x.Name);

And then:

private static bool TypeHasProperty(Expression propertyExpression)
{
    var expression = GetMemberInfo(propertyExpression);
    string name = expression.Member.Name;

    return typeof(t).GetProperty(name) != null;
}

private static MemberExpression GetMemberInfo(Expression method)
{
    LambdaExpression lambda = method as LambdaExpression;
    if (lambda == null)
        throw new ArgumentNullException("method");

    MemberExpression memberExpr = null;

    if (lambda.Body.NodeType == ExpressionType.Convert)
    {
        memberExpr = 
            ((UnaryExpression)lambda.Body).Operand as MemberExpression;
    }
    else if (lambda.Body.NodeType == ExpressionType.MemberAccess)
    {
        memberExpr = lambda.Body as MemberExpression;
    }

    if (memberExpr == null)
        throw new ArgumentException("method");

    return memberExpr;
}

Taken shamelessly from here: Retrieving Property name from lambda expression

But this is a slightly different question, so I've posted a complete answer.

I've just noticed that in the link I've provided the second answer is much simpler to this. This is covered by ChrisWue'a answer in this thread.

Community
  • 1
  • 1
BartoszKP
  • 34,786
  • 15
  • 102
  • 130