In the hallways of building 42, several of us have talked about how often we need to hard-code member names as strings in code, but we always feel like there should be some language operator to do this. We’ve specifically said there should be a memberof() in C# the same way there’s a typeof() operator.
I got tired of waiting for it though, and decided to see how closely I could come to creating this behavior. What sparked it was my usage of NHibernate’s Restrictions class tonight. I was looking at a post on Ayende’s blog showing examples of queries such as the following:
var blogs = s.CreateCriteria<Blog>()
.Add(Restrictions.Disjunction()
.Add(Restrictions.Eq("Title", "Ayende @ Rahien"))
.Add(Restrictions.Eq("Subtitle", "Send me a patch for that")))
.List<Blog>();
The hard-coded strings just make me sad; probably more so than they should, but I can’t help it. Here’s what I really want to see:
var blogs = s.CreateCriteria<Blog>()
.Add(Restrictions.Disjunction()
.Add(Restrictions.Eq(memberof(Blog.Title), "Ayende @ Rahien"))
.Add(Restrictions.Eq(memberof(Blog.Subtitle), "Send me a patch for that")))
.List<Blog>();
I obviously can’t add a memberof operator, and I also can’t make Blog.Title or Blog.Subtitle work. But this isn’t too far off:
Blog blog = null;
var blogs = s.CreateCriteria<Blog>()
.Add(Restrictions.Disjunction()
.Add(Restrictions.Eq(Member.Of(() => blog.Title), "Ayende @ Rahien"))
.Add(Restrictions.Eq(Member.Of(() => blog.Subtitle), "Send me a patch for that")))
.List<Blog>();
It’s a bit more verbose, but the member is now checked at compile time. Here’s a link to the unit tests that I have for my Member class: http://codepaste.net/t3oznt. And here’s the link to the current code for this Member class: http://codepaste.net/tfwmsu.
Here are my favorite tests:
[TestMethod]
public void CanGetMemberInfo()
{
Patron patron = null;
MemberInfo member = Member.Of(() => patron.FirstName);
Assert.IsInstanceOfType(member, typeof(MemberInfo));
Assert.AreEqual<string>("FirstName", member.Name);
}
[TestMethod]
public void CanGetMemberAsString()
{
Patron patron = null;
string member = Member.Of(() => patron.FirstName);
Assert.AreEqual<string>("FirstName", member);
}
[TestMethod]
public void PropertyChangedEventArgsIsEasy()
{
PropertyChangedEventArgs args = new PropertyChangedEventArgs(Member.Of(() => this.SomeProperty));
Assert.AreEqual<string>("SomeProperty", args.PropertyName);
}
[TestMethod]
public void RaisePropertyChangedIsEasy()
{
string propertyName = this.RaisePropertyChanged(Member.Of(() => this.SomeProperty));
Assert.AreEqual<string>("SomeProperty", propertyName);
}
public string SomeProperty { get; set; }
private string RaisePropertyChanged(string propertyName)
{
return propertyName;
}
Glenn and I had previously created a RaisePropertyChanged() method that accepted an Expression<Func<object>> to do this same thing basically, but I really like having Member.Of() as a general-purpose method that will implicitly convert to either a string or a MemberInfo, allowing me to use it anywhere that I need to specify a member name or info.
So now that I’m all excited about this, please burst my bubble and tell me why I shouldn’t do this. But if you like the idea of a memberof() being built into the language to do this, do speak up. Maybe we’ll convince Anders of it!