Various parts of the .NET framework require the use of the string name of a property:
ArgumentException
uses the name of the offending variableDependencyProperty
uses the name of the property it backsINotifyPropertyChanged
uses the name of the property which just changed.The easiest approach to populate these parameters seems to be hard coding them (ie: new ArgumentNullException("myArg")
). This seems excessively fragile it's not until runtime that you'll realize your refactoring broke the association.
Using reflection to validate these parameters is the only solution that jumps out to me but said validation is till only performed at run-time.
Is there a better way of defining the relationship between a member and it's name? Preference will be given to a simple but elegant design-time enforcement.
You can use an Expression statement to link the name.
throw new SomeException<MyType>(x => x.myArg)
Where the expression follows Expression<Func<TSource, TValue>>
. Then, refactorings and what not actually change the expression. Your exception then parses the expression and pulls the name from the property.
You can just write your reflection code to do the validation, and then run it in a post-build step. That way the validation step becomes part of the compilation process and is reported pretty much like a compile error.
(However, beware of a bug in Visual Studio due to which it is still possible to run a failed build by simply pressing “Run” a second time without making any changes...)
How are you planning to do the validation? If you want to use just the .NET’s built-in reflection capabilities, you will have to examine the method IL as raw bytes. This ILReader class will help you turn those bytes into something meaningful that you can analyse.
You can use an Expression<Func<T, object>>
to do this as follows:
using System;
using System.Linq;
using System.Linq.Expressions;
namespace ConsoleApplication3
{
public class MyClass
{
public int IntegralValue { get; set; }
public void Validate()
{
if (this.IntegralValue < 0)
throw new ArgumentOutOfRangeException(PropertyHelper.GetName<MyClass>(o => o.IntegralValue));
}
}
public static class PropertyHelper
{
/// <summary>Extracts the property (member) name from the provided expression.</summary>
public static string GetName<T>(this Expression<Func<T, object>> expression)
{
MemberExpression memberExpression = null;
if (expression.Body is MemberExpression)
memberExpression = (MemberExpression)expression.Body;
else if (expression.Body is UnaryExpression)
memberExpression = (((UnaryExpression)expression.Body).Operand as MemberExpression);
if (memberExpression == null)
throw new ApplicationException("Could not determine member name from expression.");
return memberExpression.Member.Name;
}
}
public static class Program
{
public static void Main(string[] args)
{
MyClass good = new MyClass() { IntegralValue = 100 };
MyClass bad = new MyClass() { IntegralValue = -100 };
try { good.Validate(); }
catch (Exception ex)
{
Console.WriteLine(ex);
}
try { bad.Validate(); }
catch (Exception ex)
{
Console.WriteLine(ex);
}
Console.ReadKey();
}
}
}
System.ArgumentOutOfRangeException: Specified argument was out of the range of valid values.
Parameter name: IntegralValue
at ConsoleApplication3.MyClass.Validate() in d:\...\ConsoleApplication3\Program.cs:line 14
at ConsoleApplication3.Program.Main(String[] args) in d:\...\ConsoleApplication3\Program.cs:line 50
This will allow you to use a lambda to reference property names. The GetName
method inspects the provided expression and extracts the name of the member that you've specified. This way, when you rename a property and refactor the change, all of these lambdas are automatically updated. No more strings needed!
If you love us? You can donate to us via Paypal or buy me a coffee so we can maintain and grow! Thank you!
Donate Us With