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);
}
My code wants to iterate a Dictionary that contains both FieldInfo and PropertyInfo of a type, and use that to map the values from one object to another. For example:
public static void MapFieldsAndProperties(object source, object target)
{
Dictionary<string, MemberInfo> target_properties = ClassUtils.GetPropertiesAndFields(target);
Dictionary<string, MemberInfo> source_properties = ClassUtils.GetMatchingPropertiesAndFields(target_properties.Keys, source);
foreach (var entry in source_properties)
{
var sourceProperty = entry.Value;
var targetProperty = target_properties[entry.Key];
// for now, match datatypes directly
if (dataTypesMatch(source, target))
{
var sourceValue = sourceProperty.GetValue(source);
try
{
targetProperty.SetValue(target, sourceValue);
}
catch (TargetException e)
{
LOG.ErrorFormat("unable to set value {0} for property={1}, ex={2}", sourceValue, targetProperty, e);
}
}
}
}
The problems with the above are:
1) The dataTypesMatch() function requires 2 different method signatures one for FieldInfo and one for PropertyInfo (and then to check the type of each and cast appropriately to dispatch to correct function). This is because to check Field data type uses FieldInfo.FieldType while data type for Property uses PropertyInfo.PropertyType.
2) Even though both FieldInfo and PropertyInfo have SetValue and GetValue methods, they do not derive from a common parent class, so it again requires a cast. (Maybe Dynamic would take care of this problem?)
Is there a solution which allows treating these 2 types of MemberInfo objects generically to check DataType and to Get/SetValue?
Why not just modify the method to take two arguments of type Type, and pass the FieldInfo.FieldType and PropertyInfo.PropertyType accordingly?
Since there didn't seem to be any native solution, I wrapped the PropertyInfo and FieldInfo objects in an interface which enabled the client code to use their relevant properties and methods without having to branch and cast them in the main body of the code.
public interface IGetterSetter
{
Type DataType { get; }
string Name { get; }
MemberInfo UnderlyingMember { get; }
bool CanRead { get; }
bool CanWrite { get; }
object GetValue(object obj);
void SetValue(object obj, object value);
}
So the loop to copy public Field and Property values to a target object now looks like this:
public static void Copy(object source, object target, ObjectMapperCopyValidator rules)
{
Dictionary<string, IGetterSetter> target_properties = ClassUtils.GetGetterSetters(target);
Dictionary<string, IGetterSetter> source_properties = ClassUtils.GetMatchingGetterSetters(target_properties.Keys, source);
foreach (var entry in source_properties)
{
var sourceProperty = entry.Value;
var targetProperty = target_properties[entry.Key];
// for now, match datatypes directly
if (sourceProperty.DataType == targetProperty.DataType)
{
// if property not readable or writeable, skip
if (!(sourceProperty.CanRead && targetProperty.CanWrite))
{
continue;
}
var sourceValue = sourceProperty.GetValue(source);
try
{
if (rules.IsValid(sourceProperty, sourceValue))
{
targetProperty.SetValue(target, sourceValue);
}
}
catch (TargetException e)
{
LOG.ErrorFormat("unable to set value {0} for property={1}, ex={2}", sourceValue, targetProperty, e);
}
}
}
}
Wrapping PropertyInfo and FieldInfo into the common interface was the simplest part:
public static Dictionary<string, IGetterSetter> GetGetterSetters(object target)
{
return target.GetType().GetMembers().Where(x => x.MemberType == MemberTypes.Field || x.MemberType == MemberTypes.Property).ToDictionary(x => x.Name, x => GetterSetterFactory.Instance.Create(x));
}
So the ugliness of normalizing and casting PropertyInfo and FieldInfo is hidden in the GetterSetterFactory.Instance.Create(x) method.
I also ran into this problem, but I didn't want to use an interface, because interfaces can come with steep performance penalties in tight loops. Since I'm writing a serializer that needs to be as fast as possible, I went with an extension method solution to MemberInfo.
public static void SetValue(this MemberInfo mi, object targetObject, object value)
{
switch (mi.MemberType)
{
case MemberTypes.Field:
try
{
(mi as FieldInfo).SetValue(targetObject, value);
}
catch(Exception e)
{
throw new GeneralSerializationException($"Could not set field {mi.Name} on object of type {targetObject.GetType()}.", e);
}
break;
case MemberTypes.Property:
try
{
(mi as PropertyInfo).SetValue(targetObject, value);
}
catch(Exception e)
{
throw new GeneralSerializationException($"Could not set property {mi.Name} on object of type {targetObject.GetType()}.", e);
}
break;
default:
throw new GeneralSerializationException($"MemberInfo must be a subtype of FieldInfo or PropertyInfo.");
}
}
So now you can just call MemberInfo.SetValue(object, value).
You could setup additional extension methods for the other members and methods you need access to.
UPDATE 8.31.2019: I updated the code to be more robust with more meaningful error reporting.
I'm trying to write a function which creates an object of Type t and assign its properties.
internal static object CreateInstanceWithParam(Type t, dynamic d)
{
//dynamic obj = t.GetConstructor(new Type[] { d }).Invoke(new object[] { d });
dynamic obj = t.GetConstructor(new Type[] { }).Invoke(new object[] { });
foreach (var prop in d.GetType().GetProperties(BindingFlags.Instance | BindingFlags.Public))
{
//prop.Name,
//prop.GetValue(d, null);
// assign the properties and corresponding values to newly created object ???
}
return obj;
}
Then I should be able to use this for any kind of class types like
IUser user = (IUser)CreateInstanceWithParam(userType, new { UserID = 0, Name = "user abc", LoginCode = "abc", DefaultPassword = "xxxxxx" });
IUnit newUnit = (IUnit)CreateInstanceWithParam(unitType, new { ID = 3, Code = "In", Name = "Inch", Points = "72" })
How can I assign the property prop.Name to obj?
Assuming you're just trying to copy properties, you don't need dynamic at all:
internal static object CreateInstanceWithParam(Type type, object source)
{
object instance = Activator.CreateInstance(type);
foreach (var sourceProperty in d.GetType()
.GetProperties(BindingFlags.Instance |
BindingFlags.Public))
{
var targetProperty = type.GetProperty(sourceProperty.Name);
// TODO: Check that the property is writable, non-static etc
if (targetProperty != null)
{
object value = sourceProperty.GetValue(source);
targetProperty.SetValue(instance, value);
}
}
return instance;
}
Actually, using dynamic would probably be a bad thing here; the objects you are passing in are instances of anonymous types - no need for dynamic. In particular, dynamic member access is not the same as reflection, and you cannot guarantee that an object described as dynamic will return anything interesting from .GetType().GetProperties(); consider ExpandoObject, etc.
However, FastMember (available on NuGet) may be useful:
internal static object CreateInstanceWithParam(Type type, object template)
{
TypeAccessor target = TypeAccessor.Create(type),
source = TypeAccessor.Create(template.GetType());
if (!target.CreateNewSupported)
throw new InvalidOperationException("Cannot create new instance");
if (!source.GetMembersSupported)
throw new InvalidOperationException("Cannot enumerate members");
object obj = target.CreateNew();
foreach (var member in source.GetMembers())
{
target[obj, member.Name] = source[template, member.Name];
}
return obj;
}
In particular, this can use the dynamic API just as easily as the reflection API, and you never usually see the difference.
I am using very similar loops to iterate all public fields and properties of any passed object. I determine if the field/property is decorated with a particular custom attribute; if so, an action is performed on the value of the field or property. Two loops are necessary because the method to get a field value is different from the method to get a property value.
// Iterate all public fields using reflection
foreach (FieldInfo fi in obj.GetType().GetFields())
{
// Determine if decorated with MyAttribute.
var attribs = fi.GetCustomAttributes(typeof(MyAttribute), true);
if (attribs.Length == 1)
{
// Get value of field.
object value = fi.GetValue(obj);
DoAction(value);
}
}
// Iterate all public properties using reflection
foreach (PropertyInfo pi in obj.GetType().GetProperties())
{
// Determine if decorated with MyAttribute.
var attribs = pi.GetCustomAttributes(typeof(MyAttribute), true);
if (attribs.Length == 1)
{
// Get value of property.
object value = pi.GetValue(obj, null);
DoAction(value);
}
}
I would like to place the loop in a single, common method so that I can instead write, more simply:
DoEachMember(obj.GetType().GetFields());
DoEachMember(obj.GetType().GetProperties());
This requires DoEachMember() to accept the MemberInfo type (which is the parent type of both FieldInfo and PropertyInfo). The problem is there is no GetValue method in the MemberInfo class. Both FieldInfo and PropertyInfo use different methods to get the field/property value:
public void DoEachMember(MemberInfo mi, object obj)
{
foreach (MemberInfo mi in obj.GetType().GetProperties())
{
object value mi.GetValue(obj); // NO SUCH METHOD!
}
}
Thus, I declare a delegate to utilize inside the loop which takes a MemberInfo and returns the value of that member as an object:
// Delegate to get value from field or property.
delegate object GetValue(MemberInfo mi, object obj);
The Question
How can I detect the type of the objects in the members[] array, in order to define the delegate used inside the loop? Currently, I am using the first element of the array, members[0]. Is this a good design?
public void DoEachMember(MemberInfo[] members, object obj)
{
// Protect against empty array.
if (members.Length == 0) return;
GetValue getValue; // define delegate
// First element is FieldInfo
if (members[0] as FieldInfo != null)
getValue = (mi, obj) => ((FieldInfo)mi).GetValue(obj);
// First element is PropertyInfo
else if (members[0] as PropertyInfo != null)
getValue = (mi, obj) => ((PropertyInfo)mi).GetValue(obj, null);
// Anything else is unacceptable
else
throw new ArgumentException("Must be field or property.");
foreach (MemberInfo mi in members)
{
// Determine if decorated with MyAttribute.
var attribs = mi.GetCustomAttributes(typeof(MyAttribute), true);
if (attribs.Length == 1)
{
object value = getValue(mi, obj);
DoStuff(value);
}
}
}
Alternatively, I could detect the type upon each iteration, but there should be no reason individual array members will ever differ:
foreach (MemberInfo mi in members)
{
// ...
object value;
if ((var fi = mi as FieldInfo) != null)
value = fi.GetValue(obj);
else if ((var pi = mi as PropertyInfo) != null)
value = pi.GetValue(obj, null);
else
throw new ArgumentException("Must be field or property.");
DoStuff(value);
}
You could project to the object values first and then work on those in your loop. Your whole code could be boiled down to this (plus your loop):
/// <summary>
/// Gets the value of all the fields or properties on an object that are decorated with the specified attribute
/// </summary>
private IEnumerable<object> GetValuesFromAttributedMembers<TAttribute>(object obj)
where TAttribute : Attribute
{
var values1 = obj.GetType().GetFields()
.Where(fi => fi.GetCustomAttributes(typeof(TAttribute), true).Any())
.Select(fi => fi.GetValue(obj));
var values2 = obj.GetType().GetProperties()
.Where(pi => pi.GetCustomAttributes(typeof(TAttribute), true).Any())
.Select(pi => pi.GetValue(obj, null));
return values1.Concat(values2);
}
Your current code mixes two concerns: finding the values and doing something with them. It would be cleaner to separate those concerns. The above LINQ could be placed in one method that fetches all values from a class that are in fields or properties that match a given attribute and another than is just a loop doing the work on whatever it is passed.
Not as clean but sticking with your original goal you could do this and pass in a delegate appropriate to the type of the MemberInfo you are retrieving:-
public void DoEachMember<TAttribute, TMembertype>(IEnumerable<TMembertype> members,
Func<TMembertype, object> valueGetter)
where TMembertype : MemberInfo
{
foreach (var mi in members)
{
if (mi.GetCustomAttributes(typeof(TAttribute), true).Any())
{
// Get value of field.
object value = valueGetter(mi);
DoAction(value);
}
}
}
You should use generics:
public void DoEachMember<T>(T[] members, object obj) where T: MemberInfo
{
}
Inside, test what T is, and decide which method to call based on that:
if(typeof(T)==PropertyInfo.GetType()) ...
You can obviously do the check just once, and not every iteration.
I approached this by wrapping MemberInfo in an interface like so:
public interface IMemberInfo
{
MemberInfo Wrapped { get; }
object GetValue( object obj );
void SetValue( object obj, object value );
}
internal abstract class MemberInfoWrapper : IMemberInfo
{
protected readonly MemberInfo MemberInfo;
public MemberInfoWrapper( MemberInfo memberInfo )
{
MemberInfo = memberInfo;
}
public abstract object GetValue( object obj );
public abstract void SetValue( object obj, object value );
public virtual MemberInfo Wrapped
{
get { return MemberInfo; }
}
}
internal class PropertyInfoWrapper : MemberInfoWrapper
{
public PropertyInfoWrapper( MemberInfo propertyInfo ) : base( propertyInfo )
{
Debug.Assert( propertyInfo is PropertyInfo );
}
public override object GetValue( object obj )
{
return ( (PropertyInfo)MemberInfo ).GetValue( obj, null );
}
public override void SetValue( object obj, object value )
{
( (PropertyInfo)MemberInfo ).SetValue( obj, value, null );
}
}
internal class FieldInfoWrapper : MemberInfoWrapper
{
public FieldInfoWrapper( MemberInfo fieldInfo ) : base( fieldInfo )
{
Debug.Assert( fieldInfo is FieldInfo );
}
public override object GetValue( object obj )
{
return ( (FieldInfo)MemberInfo ).GetValue( obj );
}
public override void SetValue( object obj, object value )
{
( (FieldInfo)MemberInfo ).SetValue( obj, value );
}
}
And a factory:
internal static class MemberInfoWrapperFactory
{
public static IMemberInfo CreateWrapper( this MemberInfo memberInfo )
{
switch ( memberInfo.MemberType )
{
case MemberTypes.Property:
return new PropertyInfoWrapper( memberInfo );
case MemberTypes.Field:
return new FieldInfoWrapper( memberInfo );
default:
return null;
}
}
}
Given this, you can, in your method:
// Iterate all public members using reflection
foreach (MemberInfo mi in obj.GetType().GetMembers().Where(x => x is PropertyInfo || x is FieldInfo))
{
// Determine if decorated with MyAttribute.
var attribs = mi.GetCustomAttributes(typeof(MyAttribute), true);
if (attribs.Length == 1)
{
// Get value of property.
object value = mi.CreateWrapper().GetValue(obj, null);
DoAction(value);
}
}
Try this:
var areProperties = members.All(m => m is PropertyInfo);
var areFields = members.All(m => m is FieldInfo);
areProperties will be true only if all items in the members[] array are PropertyInfo objects.
You can do something like this, if you're using C# 4.0
public void DoEachMember(MemberInfo[] members, object obj)
{
var properties = new List<dynamic>(); //dynamic objects list
properties.AddRange(members) ; // add all members to list of dynamics
foreach(dynamic d in porperties) //iterate over collection
{
var attribs = d.GetCustomAttributes(typeof(MyAttribute), true); //call dynamicaly
if (attribs.Length == 1)
{
// Get value of property.
object value = d.GetValue(obj, null); //call dynamically
DoAction(value);
}
}
}
Code becomes very short and straightforward.
Should work.
Good luck