Reputation: 877
I was pretty sure that this was possible, but for some reason, I can't seem to figure this out... I am trying to make an extension method off of Type, that will take in a Func to a property from that type, and extract a DefaultValue from the DefaultValueAttribute.
I can get it working, but only if I specify the type arguments for the GetDefaultValue function call. Here is my code as I have it currently:
Person entity:
public class Person
{
public string FirstName { get; set; }
[DefaultValue("1234")]
public string DOB { get; set; }
}
Calls to the method:
//Messing around in LinqPad - .Dump() is LinqPad method
//Works
//typeof(Person).GetDefaultValue<Person, string>(x=>x.DOB).Dump();
//Trying to get to work
//Can't figure out how to get it to infer that TIn is of the Type type.....
typeof(Person).GetDefaultValue(x=> x.DOB).Dump();
This is where the method calls are going to... I am just trying to figure out the means right now before I incorporate into my actual program... Error checking will come into play once I've figured out either how to do it, or to give up b/c it can't be done...
public static class Extensions
{
// Works
// public static TProperty GetDefaultValue<TModel, TProperty>(this Type type, Expression<Func<TModel, TProperty>> exp)
// {
// var property = typeof(TModel).GetProperties().ToList().Single(p => p.Name == GetFullPropertyName(exp));
// var defaultValue = (DefaultValueAttribute)property.GetCustomAttributes(typeof(DefaultValueAttribute), false).FirstOrDefault();
// return (TProperty)defaultValue.Value;
// }
//trying to get to work
//I know that I can't do the following, but it is basically what I am trying to do... I think!
public static TProperty GetDefaultValue<TProperty>(this Type type, Expression<Func<typeof(type), TProperty>> exp)
{
var property = type.GetProperties().ToList().Single(p => p.Name == GetFullPropertyName(exp));
var defaultValue = (DefaultValueAttribute)property.GetCustomAttributes(typeof(DefaultValueAttribute), false).FirstOrDefault();
return (TProperty)defaultValue.Value;
}
//GetFullPropertyName c/o: http://stackoverflow.com/users/105570/
//ref: http://stackoverflow.com/questions/2789504/
public static string GetFullPropertyName<TModel, TProperty>(Expression<Func<TModel, TProperty>> exp)
{
MemberExpression memberExp;
if (!TryFindMemberExpression(exp.Body, out memberExp))
return String.Empty;
var memberNames = new Stack<string>();
do
memberNames.Push(memberExp.Member.Name);
while (TryFindMemberExpression(memberExp.Expression, out memberExp));
return String.Join(".", memberNames.ToArray());
}
private static bool TryFindMemberExpression(Expression exp, out MemberExpression memberExp)
{
memberExp = exp as MemberExpression;
if (memberExp != null)
return true;
if (IsConversion(exp) && exp is UnaryExpression)
{
memberExp = ((UnaryExpression)exp).Operand as MemberExpression;
if (memberExp != null)
return true;
}
return false;
}
private static bool IsConversion(Expression exp)
{
return exp.NodeType == ExpressionType.Convert || exp.NodeType == ExpressionType.ConvertChecked;
}
}
Am I crazy, or is this actually possible? Thank you in advance for your help!
Upvotes: 3
Views: 635
Reputation: 51339
That's not how it works- typeof(Person)
doesn't have a property called DOB
, but a class whose type is Person does. What you want is to make your extension method generic:
public static TValue GetDefaultValue<TClass, TValue>(this TClass val, Expression<Func<TClass, TValue>> getter) {
var type = typeof(TClass);
var property = type.GetProperties().ToList().Single(p => p.Name == GetFullPropertyName(exp));
var defaultValue = (DefaultValueAttribute)property.GetCustomAttributes(typeof(DefaultValueAttribute), false).FirstOrDefault();
return (TProperty)defaultValue.Value;
}
and call it something like:
Person somePerson = GetMeAPerson();
somePerson.GetDefaultValue(p=>p.DOB);
Note that I have not tested the above, but I've seen similar code in the past that worked. That said, I suspect that what you were originally trying to do isn't very appealing this way because you need to make a Person instance first.
Another, possibly more appealing approach is to not make it an extension method at all:
public static TValue GetDefaultValue<TClass, TValue>(Expression<Func<TClass, TValue>> getter) {
var type = typeof(TClass);
var property = type.GetProperties().ToList().Single(p => p.Name == GetFullPropertyName(exp));
var defaultValue = (DefaultValueAttribute)property.GetCustomAttributes(typeof(DefaultValueAttribute), false).FirstOrDefault();
return (TProperty)defaultValue.Value;
}
Then you can call it without an instance, but the inference isn't as nice):
var defaultValue = GetDefaultValue<Person, DateTime>(p => p.DOB);
Upvotes: 2