I am trying to initialize objects for tests purposes. After initializing an object, I want to assert that all properties whose underlying type is a value type have a value that is not the default value of the property's type, with certain exceptions. I am not trying to recurse into sub-objects. For this purpose, I have the following:
public static void NoValueTypedPropertyHasDefaultValue<T>(this T t, params string[] exceptions)
where T: class
{
foreach (var property in typeof(T).GetProperties())
{
var propertyType = property.PropertyType;
if (propertyType.IsValueType && !exceptions.Any(x => x == property.Name))
{
var defaultValue = Activator.CreateInstance(propertyType);
object actualValue = property.GetValue(t);
Assert.NotEqual(defaultValue, actualValue);
}
}
}
Provided that everything has a parameterless constructor, it works. I'm not thrilled with the way I'm passing in strings for the exceptions. Is there a better way to do that?
You mentioned in the comments:
I'm not thrilled that the caller could just type in the string, in which case it would not survive a renaming of the property.
Making use of System.Linq.Expressions can give you compile-time safety allowing code such as:
var inst = new MyType { MyInt1 = 1 }; // Set MyInt1 but not MyInt2
inst.NoValueTypedPropertyHasDefaultValue(x => x.MyInt2); // Ignore MyInt2 from validation
The code looks like below (H/T to Retrieving Property name from lambda expression) :
public static class Extensions
{
public static void NoValueTypedPropertyHasDefaultValue<T>(this T t, params Expression<Func<T,object>>[] propertiesToIgnore)
where T : class
{
var propertyNamesToIgnore = propertiesToIgnore.Select(x => GetMemberInfo(x).Member.Name).ToArray();
foreach (var property in typeof(T).GetProperties())
{
var propertyType = property.PropertyType;
if (propertyType.IsValueType && !propertyNamesToIgnore.Contains(property.Name))
{
var defaultValue = Activator.CreateInstance(propertyType);
object actualValue = property.GetValue(t);
Assert.NotEqual(defaultValue, actualValue);
}
}
}
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;
}
}
Live example: https://dotnetfiddle.net/W9Q4gN (Note I have replaced your Assert call with an exception just for testing)
However, if all of that looks a little bit crazy/overkill to you (and to some extent, it really is!!) remember you could have just used nameof with your original scheme. Using my example your code would have become:
var inst = new MyType { MyInt1 = 1 }; // Set MyInt1 but not MyInt2
inst.NoValueTypedPropertyHasDefaultValue(nameof(inst.MyInt2)); // Ignore MyInt2 from validation
I have a lambda expression x => x.person.parent.Id == someId and I want to check if someId is null or not. The issue I am facing is that Id property is a nullable integer (i.e. int?). This is my attempt but it does not work if property is nullable. Any help would be greatly appreciated. Thank you.
private static bool IsNullExpression(Expression exp)
{
if (exp is ConstantExpression constantExpression)
{
return constantExpression.Value == null;
}
return false;
}
public void AddIfNotNull(Expression<Func<T, bool>> exp)
{
// convert expression to binary expression
if (exp.Body is BinaryExpression binaryExpression && (IsNullExpression(binaryExpression.Left) || IsNullExpression(binaryExpression.Right)))
{
// there is a null in expression, we just found it
return;
}
_list.Add(exp);
}
There are several cases you don't handle.
Firstly, if the property and the value are of different types, the constant will be warped in a Convert LINQ node, (which is of type UnaryExpression) which you need to unwrap.
Secondly if we are dealing with captured variables, the compiler will generate an extra object and the variables will be transformed into fields on this object, and the expression that accesses the variable will be a member access to the constant that contains the capture object.
private static bool IsNullExpression(Expression exp)
{
// If types are different for example int and int? there will be an extra conversion expression, we need to unwrap this
if (exp is UnaryExpression uExp) exp = uExp.Operand;
// If we are dealing with a captured variable, then teh constant will be the capture object and the value is stored as a member on this object
if (exp is MemberExpression mExp && mExp.Expression is ConstantExpression cExp)
{
object value = mExp.Member is PropertyInfo pInfo ? pInfo.GetValue(cExp.Value) :
mExp.Member is FieldInfo fInfo ? fInfo.GetValue(cExp.Value) :
throw new NotSupportedException();
return value == null;
}
// If we use a simple constant, this is what will be called
if (exp is ConstantExpression constantExpression)
{
return constantExpression.Value == null;
}
return false;
}
// Tested with the following
// Simple constant expressions
TestMethod(p => p.Id == 0);
TestMethod(p => p.Id == null);
// Capture a non null value
int value = 0;
TestMethod(p => p.Id == value);
// Capture a null value
int? nullValue = null;
TestMethod(p => p.Parent.Id == nullValue);
// Testing infrastructure
public static bool TestMethod(Expression<Func<Person, bool>> exp)
{
// If we have a binary expression
if (exp.Body is BinaryExpression binaryExpression && (IsNullExpression(binaryExpression.Left) || IsNullExpression(binaryExpression.Right)))
{
return true;
}
else
{
return false;
}
}
public class Person
{
public int? Id { get; set; }
}
I have a PropertyInfo, and I'd would like to check if it is a specific one. Knowing that its ReflectedType is correct, I could do this:
bool TestProperty(PropertyInfo prop)
{
return prop.Name == "myPropertyName";
}
The problem is that my property myPropertyName could change name in the futur, and I would have no way to realize that the above code just broke.
Is there a safe way to test what I want, probably using Expression ?
If you can make Expression for the property then it is possible to retrieve name from this expression:
public static string PropertyName<T>(this Expression<Func<T, object>> propertyExpression)
{
MemberExpression mbody = propertyExpression.Body as MemberExpression;
if (mbody == null)
{
//This will handle Nullable<T> properties.
UnaryExpression ubody = propertyExpression.Body as UnaryExpression;
if (ubody != null)
{
mbody = ubody.Operand as MemberExpression;
}
if (mbody == null)
{
throw new ArgumentException("Expression is not a MemberExpression", "propertyExpression");
}
}
return mbody.Member.Name;
}
Then you can use it in next manner:
bool TestProperty(PropertyInfo prop)
{
return prop.Name == Extensions.PropertyName<TargetClass>(x => x.myPropertyName);;
}
I am trying to create a function that takes in a object property and returns back object property.
How to get the property values into this specific Function, so that this function only takes takes in the object's specific property and not the entire object?
class Program
{
public T MapFrom<T,V>(T SourceObject.property, V DestinationObject.Property)
//Not able to achieve this//
{
// Code to Map Property
}
// Here I want to specifically pass only one property of Object , not the entire one
ProgramClassObject.MapFrom<Details,Person>(Details.FirstName,Person.FName)
}
}
// Class Containing Property
class Details
{
public string FirstName { get; set;}
}
// Class Containing Property
class Person
{
public string FName { get; set;}
}
You can do it manually, or use some library (see comments, someone mentetioned about it).
If still want to implement yourself:
Prepare some useful Expression extensions:
public static B GetProperty<T, B>(this Expression<Func<T, B>> propertySelector, T target) where T : class
{
if (target == null)
{
throw new ArgumentNullException("target");
}
if (propertySelector == null)
{
throw new ArgumentNullException("propertySelector");
}
var memberExpression = propertySelector.Body as MemberExpression;
if (memberExpression == null)
{
throw new NotSupportedException("Only member expression is supported.");
}
var propertyInfo = memberExpression.Member as PropertyInfo;
if (propertyInfo == null)
{
throw new NotSupportedException("You can select property only. Currently, selected member is: " +
memberExpression.Member);
}
return (B)propertyInfo.GetValue(target);
}
public static void SetProperty<T, B>(this Expression<Func<T, B>> propertySelector, T target, B value)
{
SetObjectProperty(target, propertySelector, value);
}
public static void SetObjectProperty<T, B>(T target, Expression<Func<T, B>> propertySelector, object value)
{
if (target == null)
{
throw new ArgumentNullException("target");
}
if (propertySelector == null)
{
throw new ArgumentNullException("propertySelector");
}
var memberExpression = propertySelector.Body as MemberExpression;
if (memberExpression == null)
{
throw new NotSupportedException("Cannot recognize property.");
}
var propertyInfo = memberExpression.Member as PropertyInfo;
if (propertyInfo == null)
{
throw new NotSupportedException("You can select property only. Currently, selected member is: " + memberExpression.Member);
}
propertyInfo.SetValue(target, value);
}
MapFrom implementation:
public static void MapFrom<TObject, TTarget, TProp>(TObject source, TTarget dest,
Expression<Func<TObject, TProp>> sourceSelector, Expression<Func<TTarget, TProp>> targetSelector)
where TObject : class where TTarget : class
{
var sourceValue = sourceSelector.GetProperty(source);
targetSelector.SetProperty(dest, sourceValue);
}
Usage:
programClassObject.MapFrom(details, person, det => det.FirstName, per => per.FName);
It sounds like what you're looking for is an expression. That's how some libraries like Entity Framework effectively "parse" the code that they're passed.
First, you can get the PropertyInfo from an expression through a method such as this. I'm going to explain how to use this below, so bear with me.
public static PropertyInfo GetPropertyInfo<TIn, TOut>(Expression<Func<TIn, TOut>> PropertyExpression)
{
MemberExpression memberExpr;
switch (PropertyExpression.Body.NodeType)
{
case ExpressionType.MemberAccess:
memberExpr = (MemberExpression)PropertyExpression.Body;
break;
case ExpressionType.Convert:
memberExpr = (MemberExpression)((UnaryExpression)PropertyExpression.Body).Operand;
break;
default:
throw new NotSupportedException();
}
var property = (PropertyInfo)memberExpr.Member;
return property;
}
Then, the method will become something like this. I've taken the liberty of ensuring the datatypes to be the same here, although you could change TOut to object if you'd prefer. I did this based on your name of MapFrom, which leads me to believe the properties are meant to "communicate" directly.
public T MapFrom<T, V, TOut>(Expression<Func<T, TOut>> Source, Expression<Func<V, TOut>> Destination)
{
var sourceProperty = GetPropertyInfo<T, TOut>(Source);
var destinationProperty = GetPropertyInfo<V, TOut>(Destination);
// Use those
// They're PropertyInfo instances, so it should be pretty easy to handle them however you would have expected to.
}
Once you've got all that,
var ret = MapFrom<Person, Details, string>(c => c.FName, c => c.FirstName);
The signature there could be cleaned up through the use of a generically typed master class, since you wouldn't have to specify any type arguments, and the string would be inferred. In a real-world situation, that's likely what you'd want to do, particularly since you appear to be, again, mapping values.
Given the following objects:
public class Customer {
public String Name { get; set; }
public String Address { get; set; }
}
public class Invoice {
public String ID { get; set; }
public DateTime Date { get; set; }
public Customer BillTo { get; set; }
}
I'd like to use reflection to go through the Invoice to get the Name property of a Customer. Here's what I'm after, assuming this code would work:
Invoice inv = GetDesiredInvoice(); // magic method to get an invoice
PropertyInfo info = inv.GetType().GetProperty("BillTo.Address");
Object val = info.GetValue(inv, null);
Of course, this fails since "BillTo.Address" is not a valid property of the Invoice class.
So, I tried writing a method to split the string into pieces on the period, and walk the objects looking for the final value I was interested in. It works okay, but I'm not entirely comfortable with it:
public Object GetPropValue(String name, Object obj) {
foreach (String part in name.Split('.')) {
if (obj == null) { return null; }
Type type = obj.GetType();
PropertyInfo info = type.GetProperty(part);
if (info == null) { return null; }
obj = info.GetValue(obj, null);
}
return obj;
}
Any ideas on how to improve this method, or a better way to solve this problem?
EDIT after posting, I saw a few related posts... There doesn't seem to be an answer that specifically addresses this question, however. Also, I'd still like the feedback on my implementation.
I use following method to get the values from (nested classes) properties like
"Property"
"Address.Street"
"Address.Country.Name"
public static object GetPropertyValue(object src, string propName)
{
if (src == null) throw new ArgumentException("Value cannot be null.", "src");
if (propName == null) throw new ArgumentException("Value cannot be null.", "propName");
if(propName.Contains("."))//complex type nested
{
var temp = propName.Split(new char[] { '.' }, 2);
return GetPropertyValue(GetPropertyValue(src, temp[0]), temp[1]);
}
else
{
var prop = src.GetType().GetProperty(propName);
return prop != null ? prop.GetValue(src, null) : null;
}
}
Here is the Fiddle: https://dotnetfiddle.net/PvKRH0
I know I'm a bit late to the party, and as others said, your implementation is fine
...for simple use cases.
However, I've developed a library that solves exactly that use case, Pather.CSharp.
It is also available as Nuget Package.
Its main class is Resolver with its Resolve method.
You pass it an object and the property path, and it will return the desired value.
Invoice inv = GetDesiredInvoice(); // magic method to get an invoice
var resolver = new Resolver();
object result = resolver.Resolve(inv, "BillTo.Address");
But it can also resolve more complex property paths, including array and dictionary access.
So, for example, if your Customer had multiple addresses
public class Customer {
public String Name { get; set; }
public IEnumerable<String> Addresses { get; set; }
}
you could access the second one using Addresses[1].
Invoice inv = GetDesiredInvoice(); // magic method to get an invoice
var resolver = new Resolver();
object result = resolver.Resolve(inv, "BillTo.Addresses[1]");
I actually think your logic is fine. Personally, I would probably change it around so you pass the object as the first parameter (which is more inline with PropertyInfo.GetValue, so less surprising).
I also would probably call it something more like GetNestedPropertyValue, to make it obvious that it searches down the property stack.
You have to access the ACTUAL object that you need to use reflection on. Here is what I mean:
Instead of this:
Invoice inv = GetDesiredInvoice(); // magic method to get an invoice
PropertyInfo info = inv.GetType().GetProperty("BillTo.Address");
Object val = info.GetValue(inv, null);
Do this (edited based on comment):
Invoice inv = GetDesiredInvoice(); // magic method to get an invoice
PropertyInfo info = inv.GetType().GetProperty("BillTo");
Customer cust = (Customer)info.GetValue(inv, null);
PropertyInfo info2 = cust.GetType().GetProperty("Address");
Object val = info2.GetValue(cust, null);
Look at this post for more information:
Using reflection to set a property of a property of an object
In hopes of not sounding too late to the party, I would like to add my solution:
Definitely use recursion in this situation
public static Object GetPropValue(String name, object obj, Type type)
{
var parts = name.Split('.').ToList();
var currentPart = parts[0];
PropertyInfo info = type.GetProperty(currentPart);
if (info == null) { return null; }
if (name.IndexOf(".") > -1)
{
parts.Remove(currentPart);
return GetPropValue(String.Join(".", parts), info.GetValue(obj, null), info.PropertyType);
} else
{
return info.GetValue(obj, null).ToString();
}
}
You don't explain the source of your "discomfort," but your code basically looks sound to me.
The only thing I'd question is the error handling. You return null if the code tries to traverse through a null reference or if the property name doesn't exist. This hides errors: it's hard to know whether it returned null because there's no BillTo customer, or because you misspelled it "BilTo.Address"... or because there is a BillTo customer, and its Address is null! I'd let the method crash and burn in these cases -- just let the exception escape (or maybe wrap it in a friendlier one).
Here is another implementation that will skip a nested property if it is an enumerator and continue deeper. Properties of type string are not affected by the Enumeration Check.
public static class ReflectionMethods
{
public static bool IsNonStringEnumerable(this PropertyInfo pi)
{
return pi != null && pi.PropertyType.IsNonStringEnumerable();
}
public static bool IsNonStringEnumerable(this object instance)
{
return instance != null && instance.GetType().IsNonStringEnumerable();
}
public static bool IsNonStringEnumerable(this Type type)
{
if (type == null || type == typeof(string))
return false;
return typeof(IEnumerable).IsAssignableFrom(type);
}
public static Object GetPropValue(String name, Object obj)
{
foreach (String part in name.Split('.'))
{
if (obj == null) { return null; }
if (obj.IsNonStringEnumerable())
{
var toEnumerable = (IEnumerable)obj;
var iterator = toEnumerable.GetEnumerator();
if (!iterator.MoveNext())
{
return null;
}
obj = iterator.Current;
}
Type type = obj.GetType();
PropertyInfo info = type.GetProperty(part);
if (info == null) { return null; }
obj = info.GetValue(obj, null);
}
return obj;
}
}
based on this question and on
How to know if a PropertyInfo is a collection
by Berryl
I use this in a MVC project to dynamically Order my data by simply passing the Property to sort by
Example:
result = result.OrderBy((s) =>
{
return ReflectionMethods.GetPropValue("BookingItems.EventId", s);
}).ToList();
where BookingItems is a list of objects.
> Get Nest properties e.g., Developer.Project.Name
private static System.Reflection.PropertyInfo GetProperty(object t, string PropertName)
{
if (t.GetType().GetProperties().Count(p => p.Name == PropertName.Split('.')[0]) == 0)
throw new ArgumentNullException(string.Format("Property {0}, is not exists in object {1}", PropertName, t.ToString()));
if (PropertName.Split('.').Length == 1)
return t.GetType().GetProperty(PropertName);
else
return GetProperty(t.GetType().GetProperty(PropertName.Split('.')[0]).GetValue(t, null), PropertName.Split('.')[1]);
}
if (info == null) { /* throw exception instead*/ }
I would actually throw an exception if they request a property that doesn't exist. The way you have it coded, if I call GetPropValue and it returns null, I don't know if that means the property didn't exist, or the property did exist but it's value was null.
public static string GetObjectPropertyValue(object obj, string propertyName)
{
bool propertyHasDot = propertyName.IndexOf(".") > -1;
string firstPartBeforeDot;
string nextParts = "";
if (!propertyHasDot)
firstPartBeforeDot = propertyName.ToLower();
else
{
firstPartBeforeDot = propertyName.Substring(0, propertyName.IndexOf(".")).ToLower();
nextParts = propertyName.Substring(propertyName.IndexOf(".") + 1);
}
foreach (var property in obj.GetType().GetProperties())
if (property.Name.ToLower() == firstPartBeforeDot)
if (!propertyHasDot)
if (property.GetValue(obj, null) != null)
return property.GetValue(obj, null).ToString();
else
return DefaultValue(property.GetValue(obj, null), propertyName).ToString();
else
return GetObjectPropertyValue(property.GetValue(obj, null), nextParts);
throw new Exception("Property '" + propertyName.ToString() + "' not found in object '" + obj.ToString() + "'");
}
I wanted to share my solution although it may be too late. This solution is primarily to check if the nested property exists. But it can be easily tweaked to return the property value if needed.
private static PropertyInfo _GetPropertyInfo(Type type, string propertyName)
{
//***
//*** Check if the property name is a complex nested type
//***
if (propertyName.Contains("."))
{
//***
//*** Get the first property name of the complex type
//***
var tempPropertyName = propertyName.Split(".", 2);
//***
//*** Check if the property exists in the type
//***
var prop = _GetPropertyInfo(type, tempPropertyName[0]);
if (prop != null)
{
//***
//*** Drill down to check if the nested property exists in the complex type
//***
return _GetPropertyInfo(prop.PropertyType, tempPropertyName[1]);
}
else
{
return null;
}
}
else
{
return type.GetProperty(propertyName, BindingFlags.IgnoreCase | BindingFlags.Public | BindingFlags.Instance);
}
}
I had to refer to few posts to come up with this solution. I think this will work for multiple nested property types.
My internet connection was down when I need to solve the same problem, so I had to 're-invent the wheel':
static object GetPropertyValue(Object fromObject, string propertyName)
{
Type objectType = fromObject.GetType();
PropertyInfo propInfo = objectType.GetProperty(propertyName);
if (propInfo == null && propertyName.Contains('.'))
{
string firstProp = propertyName.Substring(0, propertyName.IndexOf('.'));
propInfo = objectType.GetProperty(firstProp);
if (propInfo == null)//property name is invalid
{
throw new ArgumentException(String.Format("Property {0} is not a valid property of {1}.", firstProp, fromObject.GetType().ToString()));
}
return GetPropertyValue(propInfo.GetValue(fromObject, null), propertyName.Substring(propertyName.IndexOf('.') + 1));
}
else
{
return propInfo.GetValue(fromObject, null);
}
}
Pretty sure this solves the problem for any string you use for property name, regardless of extent of nesting, as long as everything's a property.
Based on the original code from #jheddings, I have created a extension method version with generic type and verifications:
public static T GetPropertyValue<T>(this object sourceObject, string propertyName)
{
if (sourceObject == null) throw new ArgumentNullException(nameof(sourceObject));
if (string.IsNullOrWhiteSpace(propertyName)) throw new ArgumentException(nameof(propertyName));
foreach (string currentPropertyName in propertyName.Split('.'))
{
if (string.IsNullOrWhiteSpace(currentPropertyName)) throw new InvalidOperationException($"Invalid property '{propertyName}'");
PropertyInfo propertyInfo = sourceObject.GetType().GetProperty(currentPropertyName);
if (propertyInfo == null) throw new InvalidOperationException($"Property '{currentPropertyName}' not found");
sourceObject = propertyInfo.GetValue(sourceObject);
}
return sourceObject is T result ? result : default;
}
I wrote a method that received one object type as the argument from the input and returns dictionary<string,string>
public static Dictionary<string, string> GetProperties(Type placeHolderType)
{
var result = new Dictionary<string, string>();
var properties = placeHolderType.GetProperties();
foreach (var propertyInfo in properties)
{
string name = propertyInfo.Name;
string description = GetDescriptionTitle(propertyInfo);
if (IsNonString(propertyInfo.PropertyType))
{
var list = GetProperties(propertyInfo.PropertyType);
foreach (var item in list)
{
result.Add($"{propertyInfo.PropertyType.Name}_{item.Key}", item.Value);
}
}
else
{
result.Add(name, description);
}
}
return result;
}
public static bool IsNonString(Type type)
{
if (type == null || type == typeof(string))
return false;
return typeof(IPlaceHolder).IsAssignableFrom(type);
}
private static string GetDescriptionTitle(MemberInfo memberInfo)
{
if (Attribute.GetCustomAttribute(memberInfo, typeof(DescriptionAttribute)) is DescriptionAttribute descriptionAttribute)
{
return descriptionAttribute.Description;
}
return memberInfo.Name;
}
public static object GetPropertyValue(object src, string propName)
{
if (src == null) throw new ArgumentException("Value cannot be null.", "src");
if (propName == null) throw new ArgumentException("Value cannot be null.", "propName");
var prop = src.GetType().GetProperty(propName);
if (prop != null)
{
return prop.GetValue(src, null);
}
else
{
var props = src.GetType().GetProperties();
foreach (var property in props)
{
var propInfo = src.GetType().GetProperty(property.Name);
if (propInfo != null)
{
var propVal = propInfo.GetValue(src, null);
if (src.GetType().GetProperty(property.Name).PropertyType.IsClass)
{
return GetPropertyValue(propVal, propName);
}
return propVal;
}
}
return null;
}
usage: calling part
var emp = new Employee() { Person = new Person() { FirstName = "Ashwani" } };
var val = GetPropertyValue(emp, "FirstName");
above can search the property value at any level
Try inv.GetType().GetProperty("BillTo+Address");