I have a function with this code:
foreach (PropertyInfo propertyInfo in typeof(T).GetProperties()){
//SOME CODE
if (propertyInfo.CanWrite)
propertyInfo.SetValue(myCopy, propertyInfo.GetValue(obj, null), null);
}
I would avoid to check "collection" properties; to do this now I have insert this control:
if (propertyInfo.PropertyType.Name.Contains("List")
|| propertyInfo.PropertyType.Name.Contains("Enumerable")
|| propertyInfo.PropertyType.Name.Contains("Collection"))
continue;
but, It don't like me!
Which is a better way to do it?
I was thinking you might want to check the interfaces the type of the property implements. (Removed redundant interfaces, as IList inherits ICollection and ICollection inherits IEnumerable.)
static void DoSomething<T>()
{
List<Type> collections = new List<Type>() { typeof(IEnumerable<>), typeof(IEnumerable) };
foreach (PropertyInfo propertyInfo in typeof(T).GetProperties())
{
if (propertyInfo.PropertyType != typeof(string) && propertyInfo.PropertyType.GetInterfaces().Any(i => collections.Any(c => i == c)))
{
continue;
}
Console.WriteLine(propertyInfo.Name);
}
}
I added code to not reject string, as it implements IEnumerable, as well, and I figured you might want to keep those around.
In light of the redundancy of the prior list of collection interfaces, it may be simpler just to write the code like this
static void DoSomething<T>()
{
foreach (PropertyInfo propertyInfo in typeof(T).GetProperties())
{
if (propertyInfo.PropertyType != typeof(string)
&& propertyInfo.PropertyType.GetInterface(typeof(IEnumerable).Name) != null
&& propertyInfo.PropertyType.GetInterface(typeof(IEnumerable<>).Name) != null)
{
continue;
}
Console.WriteLine(propertyInfo.Name);
}
}
I would probably check against IEnumerable.
if ((typeof(string) != propertyInfo.PropertyType)
&& typeof(IEnumerable).IsAssignableFrom(propertyInfo.PropertyType))
{
continue;
}
bool isCollection = typeof(System.Collections.IEnumerable)
.IsAssignableFrom(propertyInfo.PropertyType);
Related
Below is some code I use to get the initial state of all public properties in a class for IsDirty checking.
What's the easiest way to see if a property is IEnumerable?
Cheers,
Berryl
protected virtual Dictionary<string, object> _GetPropertyValues()
{
return _getPublicPropertiesWithSetters()
.ToDictionary(pi => pi.Name, pi => pi.GetValue(this, null));
}
private IEnumerable<PropertyInfo> _getPublicPropertiesWithSetters()
{
return GetType().GetProperties().Where(pi => pi.CanWrite);
}
UPDATE
What I wound up doing was adding a few library extensions as follows
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);
}
if (typeof(IEnumerable).IsAssignableFrom(prop.PropertyType) && prop.PropertyType != typeof(string))
I agree with Fyodor Soikin but the fact that is Enumerable does not mean that is only a Collection since string is also Enumerable and returns the characters one by one...
So i suggest using
if (typeof(ICollection<>).IsAssignableFrom(pi.PropertyType))
Try
private bool IsEnumerable(PropertyInfo pi)
{
return pi.PropertyType.IsSubclassOf(typeof(IEnumerable));
}
You can also use "pattern matching". This works for both List<T> and IEnumerable<T>.
private void OutputPropertyValues(object obj)
{
var properties = obj.GetType().GetProperties();
foreach (var property in properties)
{
if (property.GetValue(obj, null) is ICollection items)
{
_output.WriteLine($" {property.Name}:");
foreach (var item in items)
{
_output.WriteLine($" {item}");
}
}
else
{
_output.WriteLine($" {property.Name}: {property.GetValue(obj, null)}");
}
}
}
This question already has an answer here:
Setting all null object parameters to string.empty
(1 answer)
Closed 5 years ago.
I'm looping over a class object that contains sub-classes to check for and alter null string values:
foreach (PropertyInfo prop in contact.GetType().GetProperties())
{
if(prop.PropertyType == typeof(string) && prop.GetValue(contact, null) == null)
{
prop.SetValue(contact, "");
}
if (prop.PropertyType.IsClass)
{
PropertyInfo[] props = prop.PropertyType.GetProperties();
foreach (PropertyInfo propint in props)
{
if (propint.PropertyType == typeof(string) && propint.GetValue(prop, null) == null)
{
propint.SetValue(prop, "");
}
}
}
}
The problem is I get the exception "Object Does Not Match Target Type" when calling propint.GetValue(prop, null), I imagine that the reference for the object there isn't correct but i'm not sure what i should put there to reference the sub-class object.
You need to pass the instance of the object containing the property you want to access:
if (prop.PropertyType.IsClass)
{
PropertyInfo[] props = prop.PropertyType.GetProperties();
var propValue = prop.GetValue(contact, null);
foreach (PropertyInfo propint in props)
{
if (propint.PropertyType == typeof(string) && propint.GetValue(propValue, null) == null)
{
propint.SetValue(propValue, "");
}
}
}
I have 2 instances of the same objects, o1, and o2. If I am doing things like
if (o1.property1 != null) o1.property1 = o2.property1
for all the properties in the object. What would be the most efficient way to loop through all properties in an Object and do that? I saw people using PropertyInfo to check nulll of the properties but it seems like they could only get through the PropertyInfo collection but not link the operation of the properties.
Thanks.
You can do this with reflection:
public void CopyNonNullProperties(object source, object target)
{
// You could potentially relax this, e.g. making sure that the
// target was a subtype of the source.
if (source.GetType() != target.GetType())
{
throw new ArgumentException("Objects must be of the same type");
}
foreach (var prop in source.GetType()
.GetProperties(BindingFlags.Instance |
BindingFlags.Public)
.Where(p => !p.GetIndexParameters().Any())
.Where(p => p.CanRead && p.CanWrite))
{
var value = prop.GetValue(source, null);
if (value != null)
{
prop.SetValue(target, value, null);
}
}
}
Judging from your example i think your looking for something like this:
static void CopyTo<T>(T from, T to)
{
foreach (PropertyInfo property in typeof(T).GetProperties())
{
if (!property.CanRead || !property.CanWrite || (property.GetIndexParameters().Length > 0))
continue;
object value = property.GetValue(to, null);
if (value != null)
property.SetValue(to, property.GetValue(from, null), null);
}
}
If you are going to use this many times, you could use a compiled expression for better performance:
public static class Mapper<T>
{
static Mapper()
{
var from = Expression.Parameter(typeof(T), "from");
var to = Expression.Parameter(typeof(T), "to");
var setExpressions = typeof(T)
.GetProperties()
.Where(property => property.CanRead && property.CanWrite && !property.GetIndexParameters().Any())
.Select(property =>
{
var getExpression = Expression.Call(from, property.GetGetMethod());
var setExpression = Expression.Call(to, property.GetSetMethod(), getExpression);
var equalExpression = Expression.Equal(Expression.Convert(getExpression, typeof(object)), Expression.Constant(null));
return Expression.IfThen(Expression.Not(equalExpression), setExpression);
});
Map = Expression.Lambda<Action<T, T>>(Expression.Block(setExpressions), from, to).Compile();
}
public static Action<T, T> Map { get; private set; }
}
And use it like this:
Mapper<Entity>.Map(e1, e2);
I have multiple large objects which each have about 60 strings. I have to trim all those strings, and I'd like to do so without having to go this.mystring = this.mystring.Trim(). Instead, I'm looking for a way to automatically have each object discover its own strings and then perform the operation.
I know a little bit about reflection, but not enough, but I think this is possible?
Also, I'm not sure if this matters, but some string properties are read-only (only have a getter), so those properties would have to be skipped.
Help?
Well, it's easy enough to get all the properties, and find out which ones are strings and writable. LINQ makes it even easier.
var props = instance.GetType()
.GetProperties(BindingFlags.Instance | BindingFlags.Public)
// Ignore non-string properties
.Where(prop => prop.PropertyType == typeof(string))
// Ignore indexers
.Where(prop => prop.GetIndexParameters().Length == 0)
// Must be both readable and writable
.Where(prop => prop.CanWrite && prop.CanRead);
foreach (PropertyInfo prop in props)
{
string value = (string) prop.GetValue(instance, null);
if (value != null)
{
value = value.Trim();
prop.SetValue(instance, value, null);
}
}
You may want to only set the property if trimming actually makes a difference, to avoid redundant computations for complex properties - or it may not be an issue for you.
There are various ways of improving the performance if necessary - things like:
Simply caching the relevant properties for each type
Using Delegate.CreateDelegate to build delegates for the getters and setters
Possibly using expression trees, although I'm not sure whether they'd help here
I wouldn't take any of those steps unless performance is actually a problem though.
Something like:
foreach (PropertyInfo prop in obj.GetType().GetProperties(
BindingFlags.Instance | BindingFlags.Public))
{
if (prop.CanRead && prop.CanWrite && prop.PropertyType == typeof(string)
&& (prop.GetIndexParameters().Length == 0)) // watch for indexers!
{
var s = (string)prop.GetValue(obj, null);
if (!string.IsNullOrEmpty(s)) s = s.Trim();
prop.SetValue(obj, s, null);
}
}
Not necessary to make IEnumerable check in the props-loop and if actual instance is a IEnumerable, props are ignored. Fix for IEnumerable part:
private void TrimWhitespace(object instance)
{
if (instance != null)
{
if (instance is IEnumerable)
{
foreach (var item in (IEnumerable)instance)
{
TrimWhitespace(item);
}
}
var props = instance.GetType()
.GetProperties(BindingFlags.Instance | BindingFlags.Public)
// Ignore indexers
.Where(prop => prop.GetIndexParameters().Length == 0)
// Must be both readable and writable
.Where(prop => prop.CanWrite && prop.CanRead);
foreach (PropertyInfo prop in props)
{
if (prop.GetValue(instance, null) is string)
{
string value = (string)prop.GetValue(instance, null);
if (value != null)
{
value = value.Trim();
prop.SetValue(instance, value, null);
}
}
else
TrimWhitespace(prop.GetValue(instance, null));
}
}
}
So to expand on this a little, I have a complex object with Lists of Lists and I wanted to traverse that and trim all of the child string objects as well. I'm posting what I did as of what I built on from #Jon did in his answer. I'm curious if there was a better way to do it or if I missed something obvious.
The objects I have are more complex than this but it should illustrate what I was trying.
public class Customer
{
public string Name { get; set; }
public List<Contact> Contacts { get; set; }
}
public class Contact
{
public string Name { get; set; }
public List<Email> EmailAddresses {get; set;}
}
public class Email
{
public string EmailAddress {get; set;}
}
private void TrimWhitespace(object instance)
{
if (instance != null)
{
var props = instance.GetType()
.GetProperties(BindingFlags.Instance | BindingFlags.Public)
// Ignore indexers
.Where(prop => prop.GetIndexParameters().Length == 0)
// Must be both readable and writable
.Where(prop => prop.CanWrite && prop.CanRead);
foreach (PropertyInfo prop in props)
{
if (instance is IEnumerable)
{
foreach (var item in (IEnumerable)instance)
{
TrimWhitespace(item);
}
}
else if (prop.GetValue(instance, null) is string)
{
string value = (string)prop.GetValue(instance, null);
if (value != null)
{
value = value.Trim();
prop.SetValue(instance, value, null);
}
}
else
TrimWhitespace(prop.GetValue(instance, null));
}
}
}
Thoughts?
its me again! :)
I've been tasked with creating a system to auditing out internal objects, my first iteration was no flexible and very slow so I'm hoping to rewrite it and really make it work how it should.
Performance for this needs to be as perfect as possible, the auditing code is probably going to be run on every object in our system when they are saved..
This code below is what I've done so far - I've profiled it using the visual studio tools and I think I've remove quite a few possible performance hits along the way..
What I really want from you guys is to review this and suggest any possible improvements, Its also worth nothing that CreateObjectFromHistory method doesn't need to be as performant as the rest, its barely going to ever get called.
Also - they keyvaluepair saving is out of my hands.
Any help would be brilliant..
Cheers :)
//Wall o-code coming up..
public static void AuditObject(ITraceable obj)
{
if (obj == null)
return;
IEnumerable<PropertyInfo> properties = GetPropertyInfo(obj);
List<SerializeableKeyValuePair<string, object>> kvpList =
new List<SerializeableKeyValuePair<string, object>>();
foreach (PropertyInfo property in properties)
{
SerializeableKeyValuePair<string, object> thisValue = new SerializeableKeyValuePair<string, object>();
thisValue.Key = property.Name;
thisValue.Value = GetPropertyValue(obj, property);
if (thisValue.Value != null)
kvpList.Add(thisValue);
}
TestObject o = CreateObjectFromHistory<TestObject>(kvpList);
}
public static T CreateObjectFromHistory<T>(List<SerializeableKeyValuePair<string, object>> history)
where T : class, ITraceable
{
T historicalObject = Activator.CreateInstance<T>();
Dictionary<string, PropertyInfo> propertys = GetPropertysAsDictionary(historicalObject);
foreach (SerializeableKeyValuePair<string, object> kvp in history)
{
if (!propertys.ContainsKey(kvp.Key))
continue;
PropertyInfo prop = propertys[kvp.Key];
if (prop == null)
continue;
var value = CoerceValue(prop.PropertyType, kvp.Value);
prop.SetValue(historicalObject, value, null);
}
return historicalObject;
}
private static object CoerceValue(Type type, object value)
{
if (type == typeof(string))
return value as string;
return null;
}
private static object GetPropertyValue(ITraceable obj, PropertyInfo property)
{
if (property.PropertyType == typeof(string))
return GetProperyValueByType<string>(property.GetValue(obj, null));
else if (property.PropertyType == typeof(DateTime))
return GetProperyValueByType<DateTime>(property.GetValue(obj, null));
return null;
}
private static IEnumerable<PropertyInfo> GetPropertyInfo(ITraceable obj)
{
List<PropertyInfo> properties;
Type objType = obj.GetType();
if (PropertyDictionary.TryGetValue(objType, out properties) == false)
{
properties = obj.GetType().GetProperties(BindingFlags.Public |
BindingFlags.Instance).ToList();
properties.RemoveAll(p => IgnoreProperty(p.GetCustomAttributes(typeof(DoNoTraceAttribute), false)));
PropertyDictionary.Add(objType, properties);
}
return properties;
}
private static Dictionary<string, PropertyInfo> GetPropertysAsDictionary(ITraceable obj)
{
return GetPropertyInfo(obj).ToDictionary(pro => pro.Name);
}
private static object GetProperyValueByType<T>(object value)
{
T actualType = (T)value;
if (actualType.Equals(default(T)))
return default(T);
//this will need further implementation
return (T)value;
}
private static bool IgnoreProperty(IEnumerable<object> p)
{
return p.AsParallel().OfType<DoNoTraceAttribute>().Any();
}
Updated Code;
private static IEnumerable<PropertyInfo> GetPropertyInfo(ITraceable obj)
{
List<PropertyInfo> properties;
Type objType = obj.GetType();
if (PropertyDictionary.TryGetValue(objType, out properties) == false)
{
properties = obj.GetType().GetProperties(BindingFlags.Public |
BindingFlags.Instance).ToList();
properties.RemoveAll(p => Attribute.IsDefined(p, typeof(DoNoTraceAttribute)));
PropertyDictionary.Add(objType, properties);
}
return properties;
}
Do this look better ?
If you use PropertyInfo.GetValue() at runtime, the performance will always be slow. To get good performance (especially for looking at lots of objects) you will need to look at something like ILGenerator or Expression - or you could just use something like FastMember and access the values via prop.Name. I really do not think IgnoreProperty is implemented well - you should just look at Attribute.IsDefined here; no need for LINQ, no need for Parallel, and no need to materialize the attributes.