I have the following method:
public bool Method(object obj){
var objType = obj.GetType();
//do some reflection to get obj properties for some logic
...
if(someCondition) {
obj = null;
return false;
}
if(condition2){ return true;}
return false;
}
Method call:
var someObj = SomeObj();
Method(someObj.someProperty);
public class SomeObj : IEntity{
public SomeProperty someProperty {get;set;} //this needs to be set to null in some conditions
}
I would like to have a general method that takes in all types of Entity objects, that's why it's of "object" type. Then I would like to get its type, and go through all the properties to perform some logic. In the end, based on certain condition, I would like to set the object passed in to null. However, the problem is that the object passed in is always a property of some other Entity object, so I can't set it to ref. How do I get around this?
Since you've already used reflection to determine property info, performance most likely isn't your top concern. You can pass the object and the prop name (since you used reflection to get the obj properties already) and set it to null.
var a = new TestA();
a.Prop.Id = 1;
Method(a, "Prop");
Console.Write(a.Prop);
public bool Method(object obj, string propName)
{
// if condition is met
obj.GetType().GetProperty(propName).SetValue(obj, null);
return false;
}
public class TestA
{
public TestB Prop { get; set; } = new TestB();
}
public class TestB
{
public int Id { get; set; }
}
In the above example, a.Prop will be null.
Pass an expression
You aren't currently passing a property into Method. You are passing the value of the property. The method has no idea what the original property is and therefore cannot set it to null.
To get around this, you can pass in an Expression representing the property instead of the property's value. Then your method can parse the expression and do whatever is needed, including reading and setting the value.
So instead of doing this
var someObj = new SomeObj();
Method(someObj.someProperty); //Obtains the value of the property and passes it
The caller would do this:
var someObj = new SomeObj();
Method(() => someObj.someProperty); //Passes an expression representing the property
To modify Method so that it can work this way, you have to do a little bit of expression parsing, but not much:
static public bool Method<T>(Expression<Func<T>> expression) where T : class
{
var memberExp = (MemberExpression)expression.Body;
var propertyInfo = memberExp.Member as PropertyInfo;
var targetObject = Expression.Lambda<Func<object>>(memberExp.Expression).Compile()();
Console.WriteLine("Old value is {0}", propertyInfo.GetValue(targetObject));
if (someCondition1)
{
propertyInfo.SetValue(targetObject, null); //Set it to null
return false;
}
}
If you plan to call this frequently and you're worried about the performance of Compile(), you can avoid the compilation with slightly more code:
static public bool Method<T>(Expression<Func<T>> expression)
{
var memberExp = (MemberExpression)expression.Body;
var propertyInfo = memberExp.Member as PropertyInfo;
var propertyExpression = memberExp.Expression as MemberExpression;
var targetObject = ((FieldInfo)propertyExpression.Member).GetValue((propertyExpression.Expression as ConstantExpression).Value);
Console.WriteLine("Old value is {0}", propertyInfo.GetValue(targetObject));
if (SomeCondition1)
{
propertyInfo.SetValue(targetObject, null);
return false;
}
}
I am trying implement the Data transformation using Reflection1 example in my code.
The GetSourceValue function has a switch comparing various types, but I want to remove these types and properties and have GetSourceValue get the value of the property using only a single string as the parameter. I want to pass a class and property in the string and resolve the value of the property.
Is this possible?
1 Web Archive version of original blog post
public static object GetPropValue(object src, string propName)
{
return src.GetType().GetProperty(propName).GetValue(src, null);
}
Of course, you will want to add validation and whatnot, but that is the gist of it.
How about something like this:
public static Object GetPropValue(this Object obj, String name) {
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;
}
public static T GetPropValue<T>(this Object obj, String name) {
Object retval = GetPropValue(obj, name);
if (retval == null) { return default(T); }
// throws InvalidCastException if types are incompatible
return (T) retval;
}
This will allow you to descend into properties using a single string, like this:
DateTime now = DateTime.Now;
int min = GetPropValue<int>(now, "TimeOfDay.Minutes");
int hrs = now.GetPropValue<int>("TimeOfDay.Hours");
You can either use these methods as static methods or extensions.
Add to any Class:
public class Foo
{
public object this[string propertyName]
{
get { return this.GetType().GetProperty(propertyName).GetValue(this, null); }
set { this.GetType().GetProperty(propertyName).SetValue(this, value, null); }
}
public string Bar { get; set; }
}
Then, you can use as:
Foo f = new Foo();
// Set
f["Bar"] = "asdf";
// Get
string s = (string)f["Bar"];
What about using the CallByName of the Microsoft.VisualBasic namespace (Microsoft.VisualBasic.dll)? It uses reflection to get properties, fields, and methods of normal objects, COM objects, and even dynamic objects.
using Microsoft.VisualBasic;
using Microsoft.VisualBasic.CompilerServices;
and then
Versioned.CallByName(this, "method/function/prop name", CallType.Get).ToString();
Great answer by jheddings. I would like to improve it by allowing referencing of aggregated arrays or collections of objects, so that propertyName could be property1.property2[X].property3:
public static object GetPropertyValue(object srcobj, string propertyName)
{
if (srcobj == null)
return null;
object obj = srcobj;
// Split property name to parts (propertyName could be hierarchical, like obj.subobj.subobj.property
string[] propertyNameParts = propertyName.Split('.');
foreach (string propertyNamePart in propertyNameParts)
{
if (obj == null) return null;
// propertyNamePart could contain reference to specific
// element (by index) inside a collection
if (!propertyNamePart.Contains("["))
{
PropertyInfo pi = obj.GetType().GetProperty(propertyNamePart);
if (pi == null) return null;
obj = pi.GetValue(obj, null);
}
else
{ // propertyNamePart is areference to specific element
// (by index) inside a collection
// like AggregatedCollection[123]
// get collection name and element index
int indexStart = propertyNamePart.IndexOf("[")+1;
string collectionPropertyName = propertyNamePart.Substring(0, indexStart-1);
int collectionElementIndex = Int32.Parse(propertyNamePart.Substring(indexStart, propertyNamePart.Length-indexStart-1));
// get collection object
PropertyInfo pi = obj.GetType().GetProperty(collectionPropertyName);
if (pi == null) return null;
object unknownCollection = pi.GetValue(obj, null);
// try to process the collection as array
if (unknownCollection.GetType().IsArray)
{
object[] collectionAsArray = unknownCollection as object[];
obj = collectionAsArray[collectionElementIndex];
}
else
{
// try to process the collection as IList
System.Collections.IList collectionAsList = unknownCollection as System.Collections.IList;
if (collectionAsList != null)
{
obj = collectionAsList[collectionElementIndex];
}
else
{
// ??? Unsupported collection type
}
}
}
}
return obj;
}
If I use the code from Ed S. I get
'ReflectionExtensions.GetProperty(Type, string)' is inaccessible due to its protection level
It seems that GetProperty() is not available in Xamarin.Forms. TargetFrameworkProfile is Profile7 in my Portable Class Library (.NET Framework 4.5, Windows 8, ASP.NET Core 1.0, Xamarin.Android, Xamarin.iOS, Xamarin.iOS Classic).
Now I found a working solution:
using System.Linq;
using System.Reflection;
public static object GetPropValue(object source, string propertyName)
{
var property = source.GetType().GetRuntimeProperties().FirstOrDefault(p => string.Equals(p.Name, propertyName, StringComparison.OrdinalIgnoreCase));
return property?.GetValue(source);
}
Source
About the nested properties discussion, you can avoid all the reflection stuff if you use the DataBinder.Eval Method (Object, String) as below:
var value = DataBinder.Eval(DateTime.Now, "TimeOfDay.Hours");
Of course, you'll need to add a reference to the System.Web assembly, but this probably isn't a big deal.
The method to call has changed in .NET Standard (as of 1.6). Also we can use C# 6's null conditional operator.
using System.Reflection;
public static object GetPropValue(object src, string propName)
{
return src.GetType().GetRuntimeProperty(propName)?.GetValue(src);
}
The below method works perfect for me:
class MyClass {
public string prop1 { set; get; }
public object this[string propertyName]
{
get { return this.GetType().GetProperty(propertyName).GetValue(this, null); }
set { this.GetType().GetProperty(propertyName).SetValue(this, value, null); }
}
}
To get the property value:
MyClass t1 = new MyClass();
...
string value = t1["prop1"].ToString();
To set the property value:
t1["prop1"] = value;
public static List<KeyValuePair<string, string>> GetProperties(object item) //where T : class
{
var result = new List<KeyValuePair<string, string>>();
if (item != null)
{
var type = item.GetType();
var properties = type.GetProperties(BindingFlags.Public | BindingFlags.Instance);
foreach (var pi in properties)
{
var selfValue = type.GetProperty(pi.Name).GetValue(item, null);
if (selfValue != null)
{
result.Add(new KeyValuePair<string, string>(pi.Name, selfValue.ToString()));
}
else
{
result.Add(new KeyValuePair<string, string>(pi.Name, null));
}
}
}
return result;
}
This is a way to get all properties with their values in a List.
Using PropertyInfo of the System.Reflection namespace. Reflection compiles just fine no matter what property we try to access. The error will come up during run-time.
public static object GetObjProperty(object obj, string property)
{
Type t = obj.GetType();
PropertyInfo p = t.GetProperty("Location");
Point location = (Point)p.GetValue(obj, null);
return location;
}
It works fine to get the Location property of an object
Label1.Text = GetObjProperty(button1, "Location").ToString();
We'll get the Location : {X=71,Y=27}
We can also return location.X or location.Y on the same way.
public class YourClass
{
//Add below line in your class
public object this[string propertyName] => GetType().GetProperty(propertyName)?.GetValue(this, null);
public string SampleProperty { get; set; }
}
//And you can get value of any property like this.
var value = YourClass["SampleProperty"];
The following code is a Recursive method for displaying the entire hierarchy of all of the Property Names and Values contained in an object's instance. This method uses a simplified version of AlexD's GetPropertyValue() answer above in this thread. Thanks to this discussion thread, I was able to figure out how to do this!
For example, I use this method to show an explosion or dump of all of the properties in a WebService response by calling the method as follows:
PropertyValues_byRecursion("Response", response, false);
public static object GetPropertyValue(object srcObj, string propertyName)
{
if (srcObj == null)
{
return null;
}
PropertyInfo pi = srcObj.GetType().GetProperty(propertyName.Replace("[]", ""));
if (pi == null)
{
return null;
}
return pi.GetValue(srcObj);
}
public static void PropertyValues_byRecursion(string parentPath, object parentObj, bool showNullValues)
{
/// Processes all of the objects contained in the parent object.
/// If an object has a Property Value, then the value is written to the Console
/// Else if the object is a container, then this method is called recursively
/// using the current path and current object as parameters
// Note: If you do not want to see null values, set showNullValues = false
foreach (PropertyInfo pi in parentObj.GetType().GetTypeInfo().GetProperties())
{
// Build the current object property's namespace path.
// Recursion extends this to be the property's full namespace path.
string currentPath = parentPath + "." + pi.Name;
// Get the selected property's value as an object
object myPropertyValue = GetPropertyValue(parentObj, pi.Name);
if (myPropertyValue == null)
{
// Instance of Property does not exist
if (showNullValues)
{
Console.WriteLine(currentPath + " = null");
// Note: If you are replacing these Console.Write... methods callback methods,
// consider passing DBNull.Value instead of null in any method object parameters.
}
}
else if (myPropertyValue.GetType().IsArray)
{
// myPropertyValue is an object instance of an Array of business objects.
// Initialize an array index variable so we can show NamespacePath[idx] in the results.
int idx = 0;
foreach (object business in (Array)myPropertyValue)
{
if (business == null)
{
// Instance of Property does not exist
// Not sure if this is possible in this context.
if (showNullValues)
{
Console.WriteLine(currentPath + "[" + idx.ToString() + "]" + " = null");
}
}
else if (business.GetType().IsArray)
{
// myPropertyValue[idx] is another Array!
// Let recursion process it.
PropertyValues_byRecursion(currentPath + "[" + idx.ToString() + "]", business, showNullValues);
}
else if (business.GetType().IsSealed)
{
// Display the Full Property Path and its Value
Console.WriteLine(currentPath + "[" + idx.ToString() + "] = " + business.ToString());
}
else
{
// Unsealed Type Properties can contain child objects.
// Recurse into my property value object to process its properties and child objects.
PropertyValues_byRecursion(currentPath + "[" + idx.ToString() + "]", business, showNullValues);
}
idx++;
}
}
else if (myPropertyValue.GetType().IsSealed)
{
// myPropertyValue is a simple value
Console.WriteLine(currentPath + " = " + myPropertyValue.ToString());
}
else
{
// Unsealed Type Properties can contain child objects.
// Recurse into my property value object to process its properties and child objects.
PropertyValues_byRecursion(currentPath, myPropertyValue, showNullValues);
}
}
}
public static TValue GetFieldValue<TValue>(this object instance, string name)
{
var type = instance.GetType();
var field = type.GetFields(BindingFlags.NonPublic | BindingFlags.Static | BindingFlags.Instance).FirstOrDefault(e => typeof(TValue).IsAssignableFrom(e.FieldType) && e.Name == name);
return (TValue)field?.GetValue(instance);
}
public static TValue GetPropertyValue<TValue>(this object instance, string name)
{
var type = instance.GetType();
var field = type.GetProperties(BindingFlags.NonPublic | BindingFlags.Static | BindingFlags.Instance).FirstOrDefault(e => typeof(TValue).IsAssignableFrom(e.PropertyType) && e.Name == name);
return (TValue)field?.GetValue(instance);
}
Dim NewHandle As YourType = CType(Microsoft.VisualBasic.CallByName(ObjectThatContainsYourVariable, "YourVariableName", CallType), YourType)
Here is another way to find a nested property that doesn't require the string to tell you the nesting path. Credit to Ed S. for the single property method.
public static T FindNestedPropertyValue<T, N>(N model, string propName) {
T retVal = default(T);
bool found = false;
PropertyInfo[] properties = typeof(N).GetProperties();
foreach (PropertyInfo property in properties) {
var currentProperty = property.GetValue(model, null);
if (!found) {
try {
retVal = GetPropValue<T>(currentProperty, propName);
found = true;
} catch { }
}
}
if (!found) {
throw new Exception("Unable to find property: " + propName);
}
return retVal;
}
public static T GetPropValue<T>(object srcObject, string propName) {
return (T)srcObject.GetType().GetProperty(propName).GetValue(srcObject, null);
}
You never mention what object you are inspecting, and since you are rejecting ones that reference a given object, I will assume you mean a static one.
using System.Reflection;
public object GetPropValue(string prop)
{
int splitPoint = prop.LastIndexOf('.');
Type type = Assembly.GetEntryAssembly().GetType(prop.Substring(0, splitPoint));
object obj = null;
return type.GetProperty(prop.Substring(splitPoint + 1)).GetValue(obj, null);
}
Note that I marked the object that is being inspected with the local variable obj. null means static, otherwise set it to what you want. Also note that the GetEntryAssembly() is one of a few available methods to get the "running" assembly, you may want to play around with it if you are having a hard time loading the type.
Have a look at the Heleonix.Reflection library. You can get/set/invoke members by paths, or create a getter/setter (lambda compiled into a delegate) which is faster than reflection. For example:
var success = Reflector.Get(DateTime.Now, null, "Date.Year", out int value);
Or create a getter once and cache for reuse (this is more performant but might throw NullReferenceException if an intermediate member is null):
var getter = Reflector.CreateGetter<DateTime, int>("Date.Year", typeof(DateTime));
getter(DateTime.Now);
Or if you want to create a List<Action<object, object>> of different getters, just specify base types for compiled delegates (type conversions will be added into compiled lambdas):
var getter = Reflector.CreateGetter<object, object>("Date.Year", typeof(DateTime));
getter(DateTime.Now);
Although the original question was about how to get the value of the property using only a single string as the parameter, it makes a lot of sense here to use an Expression rather than simply a string to ensure that the caller never uses a hard coded property name. Here is a one line version with usage:
public static class Utils
...
public static TVal GetPropertyValue<T, TVal>(T t, Expression<Func<T, TVal>> x)
=> (TVal)((x.Body as MemberExpression)?.Member as PropertyInfo)!.GetValue(t);
...
var val = Utils.GetPropertyValue(foo, p => p.Bar);
Here is a slightly better version in terms of readability a error handling:
public static TVal GetPropertyValue<T, TVal>(T t, Expression<Func<T, TVal>> x)
{
var m = (x.Body as MemberExpression)?.Member;
var p = m as PropertyInfo;
if (null == p)
throw new ArgumentException($"Unknown property: {typeof(T).Name}.{(m?.Name??"???")}");
return (TVal)p.GetValue(t);
}
In short you pass in a lambda expression reading a property. The body of the lambda - the part on the right of the fat arrow - is a member expression from which you can get the member name and which you can cast to a PropertyInfo, provided the member is actually a Property and not, for instance, a method.
In the short version, the null forgiving operator - the ! in the expression - tells the compiler that the PropertyInfo will not be null. This is a big lie and you will get a NullReferenceException at runtime. The longer version gives you the name of the property if it manages to get it.
PS: Thanks to Oleg G. for the initial version of this code :)
shorter way ....
var a = new Test { Id = 1 , Name = "A" , date = DateTime.Now};
var b = new Test { Id = 1 , Name = "AXXX", date = DateTime.Now };
var compare = string.Join("",a.GetType().GetProperties().Select(x => x.GetValue(a)).ToArray())==
string.Join("",b.GetType().GetProperties().Select(x => x.GetValue(b)).ToArray());
jheddings and AlexD both wrote excellent answers on how to resolve property strings. I'd like to throw mine in the mix, since I wrote a dedicated library exactly for that purpose.
Pather.CSharp's main class is Resolver. Per default it can resolve properties, array and dictionary entries.
So, for example, if you have an object like this
var o = new { Property1 = new { Property2 = "value" } };
and want to get Property2, you can do it like this:
IResolver resolver = new Resolver();
var path = "Property1.Property2";
object result = r.Resolve(o, path);
//=> "value"
This is the most basic example of the paths it can resolve. If you want to see what else it can, or how you can extend it, just head to its Github page.
Here's what I got based on other answers. A little overkill on getting so specific with the error handling.
public static T GetPropertyValue<T>(object sourceInstance, string targetPropertyName, bool throwExceptionIfNotExists = false)
{
string errorMsg = null;
try
{
if (sourceInstance == null || string.IsNullOrWhiteSpace(targetPropertyName))
{
errorMsg = $"Source object is null or property name is null or whitespace. '{targetPropertyName}'";
Log.Warn(errorMsg);
if (throwExceptionIfNotExists)
throw new ArgumentException(errorMsg);
else
return default(T);
}
Type returnType = typeof(T);
Type sourceType = sourceInstance.GetType();
PropertyInfo propertyInfo = sourceType.GetProperty(targetPropertyName, returnType);
if (propertyInfo == null)
{
errorMsg = $"Property name '{targetPropertyName}' of type '{returnType}' not found for source object of type '{sourceType}'";
Log.Warn(errorMsg);
if (throwExceptionIfNotExists)
throw new ArgumentException(errorMsg);
else
return default(T);
}
return (T)propertyInfo.GetValue(sourceInstance, null);
}
catch(Exception ex)
{
errorMsg = $"Problem getting property name '{targetPropertyName}' from source instance.";
Log.Error(errorMsg, ex);
if (throwExceptionIfNotExists)
throw;
}
return default(T);
}
Here is my solution. It works also with COM objects and allows to access collection/array items from COM objects.
public static object GetPropValue(this object obj, string name)
{
foreach (string part in name.Split('.'))
{
if (obj == null) { return null; }
Type type = obj.GetType();
if (type.Name == "__ComObject")
{
if (part.Contains('['))
{
string partWithoundIndex = part;
int index = ParseIndexFromPropertyName(ref partWithoundIndex);
obj = Versioned.CallByName(obj, partWithoundIndex, CallType.Get, index);
}
else
{
obj = Versioned.CallByName(obj, part, CallType.Get);
}
}
else
{
PropertyInfo info = type.GetProperty(part);
if (info == null) { return null; }
obj = info.GetValue(obj, null);
}
}
return obj;
}
private static int ParseIndexFromPropertyName(ref string name)
{
int index = -1;
int s = name.IndexOf('[') + 1;
int e = name.IndexOf(']');
if (e < s)
{
throw new ArgumentException();
}
string tmp = name.Substring(s, e - s);
index = Convert.ToInt32(tmp);
name = name.Substring(0, s - 1);
return index;
}
Whenever you want to loop over all properties in on an object and then use each value of the property must use this piece of code:
foreach (var property in request.GetType().GetProperties())
{
var valueOfProperty = property.GetValue(properties, null);
}
I have to set a property inside an unknown object. The structure looks like this:
ObjA.ObjB().ObjC.PropA = propValue;
ObjA is from a referenced class. ObjB() is of type object and therefore ObjC is unknown. I thought about using Reflection but don't know how to use it correctly in this case.
object objB = ObjA.ObjB();
Type objBType = objB.GetType();
System.Reflection.XXXInfo objCInfo = objBType.GetXXX("ObjC");
Type objCType = objCInfo.GetType();
System.Reflection.PropertyInfo PropAInfo = objCType.GetProperty("PropA");
PropAInfo.SetValue(PropAInfo, propValue, null);
Answer (Thanks to BigM):
dynamic objAB = ObjA.ObjB();
objAB.ObjC.PropA = propValue;
This should probably work for you.
object objB = ObjA.ObjB();
Type objBType = objB.GetType();
System.Reflection.PropertyInfo objCInfo = objBType.GetProperty("ObjC");
object val = objCInfo.GetValue(objB);
Type objCType = val.GetType();
System.Reflection.PropertyInfo PropAInfo = objCType.GetProperty("PropA");
PropAInfo.SetValue(val, propValue, null);
However, I think a bit of re-architecting could be done here to make life a bit easier. For example, if you don't know anything about the types then you might consider using dynamic and returning dynamic types from ObjC and PropA - but there is a performance hit there.
On the other hand, if there is any way that you can use generics, that would make your life a lot easier. For example, the code here that sets the property value, if that method were generic it might likely be able to define the type of ObjC - but I can't really infer that with the current snippet.
Here are a couple of generic extension methods to help you get and set "unknown" properties by name:
public static class ReflectionHelpers
{
public static bool TrySetProperty<TValue>(this object obj, string propertyName, TValue value)
{
var property = obj.GetType()
.GetProperties()
.Where(p => p.CanWrite && p.PropertyType == typeof(TValue))
.FirstOrDefault(p => p.Name == propertyName);
if (property == null)
{
return false;
}
property.SetValue(obj, value);
return true;
}
public static bool TryGetPropertyValue<TProperty>(this object obj, string propertyName, out TProperty value)
{
var property = obj.GetType()
.GetProperties()
.Where(p => p.CanRead && p.PropertyType == typeof(TProperty))
.FirstOrDefault(p => p.Name == propertyName);
if (property == null)
{
value = default(TProperty);
return false;
}
value = (TProperty) property.GetValue(obj);
return true;
}
}
And a usage example:
public class Program
{
public static void Main()
{
var foo = new Foo
{
Bar = new Bar
{
HelloReflection = "Testing"
}
};
string currentValue;
if (foo.Bar.TryGetPropertyValue("HelloReflection", out currentValue))
{
Console.WriteLine(currentValue); // "Testing"
}
if (foo.Bar.TrySetProperty("HelloReflection", "123..."))
{
foo.Bar.TryGetPropertyValue("HelloReflection", out currentValue)
Console.WriteLine(currentValue); // "123.."
}
else
{
Console.WriteLine("Failed to set value");
}
}
}
public class Foo
{
public object Bar { get; set; }
}
public class Bar
{
public string HelloReflection { get; set; }
}