I'm experimenting with reflection in c# for the first time (for use in a dev tool I'm building) and I was wondering if someone could help me to access the thing that I'm trying to access?
The code below is the class I'm accessing:
[Serializable]
public class MaterialVariantTarget : BaseNode, IMaterialTarget
{
public MeshRenderer m_Target;
public void ApplyValue(Material value) => m_Target.material = value;
}
The question I want to answer is what is the name of the value that ApplyValue() operate on. So in this example it would be MeshRenderer.material
I've broken the problem down into two parts. Accessing m_Target's Type (MeshRenderer) and the .material property.
The first, I've managed to access:
private Type GetTargetComponentType(Type targetNodeType)
{
foreach (var fieldInfo in targetNodeType.GetFields())
{
if (fieldInfo.Name == "m_Target")
{
var type = fieldInfo.FieldType;
return type;
}
}
return null;
}
I'm finding accessing the scond part more tricky. Is what I'm trying to do even possible and if so, how can I do it?
Many Thanks
[UPDATE]
So the consensus seems to be that I can't access the contents of the method.
I'm going to have to just resort to writing out the info I need as a string hich can then be read but its not ideal :(
Might I have more options if I were to arrange it as a property get/setter? like this:
[Serializable]
public class MaterialVariantTarget : BaseNode, IMaterialTarget
{
public MeshRenderer m_Target;
private Material m_valueProperty
{
get => m_Target.material;
set => m_Target.material = value;
}
public void ApplyValue(Material value) => m_valueProperty = value;
}
Here's two handy extensions I made to retrieve the value of a field or property of an object based on a name or a type:
public static class Extensions
{
public static object GetPropertyOrFieldByName(this object obj, string nameToSearch)
{
foreach (var field in obj.GetType().GetFields())
{
if (field.Name == nameToSearch)
{
return field.GetValue(obj);
}
}
foreach (var property in obj.GetType().GetProperties())
{
if (property.Name == nameToSearch)
{
return property.GetValue(obj);
}
}
return null;
}
public static T GetPropertyOrFieldByType<T>(this object obj) where T : Object
{
foreach (var field in obj.GetType().GetFields())
{
if (field.FieldType == typeof(T))
{
return (T)field.GetValue(obj);
}
}
foreach (var property in obj.GetType().GetProperties())
{
if (property.PropertyType == typeof(T))
{
return (T)property.GetValue(obj);
}
}
return null;
}
}
The usage you require could be implemented this way:
object target = yourMaterialVariantTarget.GetPropertyOrFieldByName("m_Target");
Material material = target.GetPropertyOrFieldByType<Material>();
material.color = Color.red;
How can I check if a given type is an implementation of ICollection<T>?
For instance, let's say we have the following variable:
ICollection<object> list = new List<object>();
Type listType = list.GetType();
Is there a way to identify if listType is a generic ICollection<>?
I have tried the following, but with no luck:
if(typeof(ICollection).IsAssignableFrom(listType))
// ...
if(typeof(ICollection<>).IsAssignableFrom(listType))
// ...
Of course, I can do the following:
if(typeof(ICollection<object>).IsAssignableFrom(listType))
// ...
But that will only work for ICollection<object> types. If I have an ICollection<string> it will fail.
You can do it like this:
bool implements =
listType.GetInterfaces()
.Any(x =>
x.IsGenericType &&
x.GetGenericTypeDefinition() == typeof (ICollection<>));
You can try use this code, it works for all collection type
public static class GenericClassifier
{
public static bool IsICollection(Type type)
{
return Array.Exists(type.GetInterfaces(), IsGenericCollectionType);
}
public static bool IsIEnumerable(Type type)
{
return Array.Exists(type.GetInterfaces(), IsGenericEnumerableType);
}
public static bool IsIList(Type type)
{
return Array.Exists(type.GetInterfaces(), IsListCollectionType);
}
static bool IsGenericCollectionType(Type type)
{
return type.IsGenericType && (typeof(ICollection<>) == type.GetGenericTypeDefinition());
}
static bool IsGenericEnumerableType(Type type)
{
return type.IsGenericType && (typeof(IEnumerable<>) == type.GetGenericTypeDefinition());
}
static bool IsListCollectionType(Type type)
{
return type.IsGenericType && (typeof(IList) == type.GetGenericTypeDefinition());
}
}
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.
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");
public bool IsList(object value)
{
Type type = value.GetType();
// Check if type is a generic list of any type
}
What's the best way to check if the given object is a list, or can be cast to a list?
For you guys that enjoy the use of extension methods:
public static bool IsGenericList(this object o)
{
var oType = o.GetType();
return (oType.IsGenericType && (oType.GetGenericTypeDefinition() == typeof(List<>)));
}
So, we could do:
if(o.IsGenericList())
{
//...
}
using System.Collections;
if(value is IList && value.GetType().IsGenericType) {
}
bool isList = o.GetType().IsGenericType
&& o.GetType().GetGenericTypeDefinition() == typeof(IList<>));
public bool IsList(object value) {
return value is IList
|| IsGenericList(value);
}
public bool IsGenericList(object value) {
var type = value.GetType();
return type.IsGenericType
&& typeof(List<>) == type.GetGenericTypeDefinition();
}
Here's an implementation that works in .NET Standard, and works against interfaces:
public static bool ImplementsGenericInterface(this Type type, Type interfaceType)
{
return type
.GetTypeInfo()
.ImplementedInterfaces
.Any(x => x.GetTypeInfo().IsGenericType && x.GetGenericTypeDefinition() == interfaceType);
}
And here are the tests (xunit):
[Fact]
public void ImplementsGenericInterface_List_IsValidInterfaceTypes()
{
var list = new List<string>();
Assert.True(list.GetType().ImplementsGenericInterface(typeof(IList<>)));
Assert.True(list.GetType().ImplementsGenericInterface(typeof(IEnumerable<>)));
Assert.True(list.GetType().ImplementsGenericInterface(typeof(IReadOnlyList<>)));
}
[Fact]
public void ImplementsGenericInterface_List_IsNotInvalidInterfaceTypes()
{
var list = new List<string>();
Assert.False(list.GetType().ImplementsGenericInterface(typeof(string)));
Assert.False(list.GetType().ImplementsGenericInterface(typeof(IDictionary<,>)));
Assert.False(list.GetType().ImplementsGenericInterface(typeof(IComparable<>)));
Assert.False(list.GetType().ImplementsGenericInterface(typeof(DateTime)));
}
if(value is IList && value.GetType().GetGenericArguments().Length > 0)
{
}
Based on Victor Rodrigues' answer, we can devise another method for generics. In fact, the original solution can be reduced to only two lines:
public static bool IsGenericList(this object Value)
{
var t = Value.GetType();
return t.IsGenericType && t.GetGenericTypeDefinition() == typeof(List<>);
}
public static bool IsGenericList<T>(this object Value)
{
var t = Value.GetType();
return t.IsGenericType && t.GetGenericTypeDefinition() == typeof(List<T>);
}
I'm using the following code:
public bool IsList(Type type) => type.IsGenericType && (
(type.GetGenericTypeDefinition() == typeof(List<>))
|| (type.GetGenericTypeDefinition() == typeof(IList<>))
);
Probably the best way would be to do something like this:
IList list = value as IList;
if (list != null)
{
// use list in here
}
This will give you maximum flexibility and also allow you to work with many different types that implement the IList interface.