Get properties of properties of a class - c#

I want to get the properties of the properties of a class.
What I have right now:
foreach (var v in test.GetType().GetProperties())
{
foreach (var p in v.GetType().GetProperties())
{
}
}
The first foreach loop works fine and gets the properties of the class variable test. However, in the second loop, I get output such as MemberType, ReflectedType, Module etc.. not actual properties.
My goal is to get the properties of the properties of a class and then edit their value (truncate them using another function).
Thanks.

On the second loop GetType() returns a PropertyInfo object. You have to get the propertyType of v as v.PropertyType.GetProperties() to achieve what you want.
So, the code should be:
foreach (var v in test.GetType().GetProperties())
{
foreach (var p in v.PropertyType.GetProperties())
{
// Stuff
}
}

The type returned by v.GetType() is that of PropertyInfo, because v is a property info. You don't want the properties of the PropertyInfo type, you want the properties of the type itself.
Use v.PropertyType, not v.GetType().

GetProperties() gets you PropertyInfo objects which tell you information about the properties of the object. You need to use GetValue to actually get the values of those properties. From there you can repeat the process to get the values of that object's properties.
foreach (var v in test.GetType().GetProperties())
{
var propertyValue = v.GetValue(test);
foreach (var p in propertyValue.GetType().GetProperties())
{
var subPropertyValue = p.GetValue(propertyValue);
Console.WriteLine("{0} = {1}", p.Name, subPropertyValue);
}
}
After editing the value use SetValue to persist it back to the object.

Related

C#: Why does my string return object type and not the value it contains?

I am looping through a List, and trying to instantiate one of the properties as a string, but it returns the type:
{Namespace.Collection}
If I put a break-point, I can see that it holds the value I need.
How can I make it return the value and not the type?
foreach (var PropertyName in ListName) {
string n = PropertyName.ToString();
}
UPDATE (added more of my code, as well as an attempt of implementing suggested solutions):
foreach (DataRow dr in ds.Tables[0].Rows) {
//PaidTrips is my ObservableCollection instance.
//PaidTrip is my holder class which it has been bound to.
PaidTrips.Add(new PaidTrip {
LicenseHolderID = dr[0].ToString(),
// adding more properties
});
List<PaidTrip> theseTrips = PaidTrips
.GroupBy(p => new { p.LicenseHolderID })
.Select(g => g.First())
.ToList();
foreach (PaidTrip PaidTrips in theseTrips) {
foreach (var LicenseHolderID in PaidTrips.GetType().GetProperties()) {
string n = LicenseHolderID.GetValue(PaidTrips).ToString();
// code to create PDF
}
gfx.DrawString(n, new XFont("Arial", 40, XFontStyle.Bold), ridelGreen, new XPoint(40, 350));
This is what I do with string n. But when the PDF is created, the string output is System.Action1[System.Action]`
What am I doing wrong?
You need to loop through the Property Types in your custom class, after looping through the list. First we need an additional loop - to loop through each ClassName Object in ListName list.
foreach (ClassName myObj in ListName)
{
foreach (var PropertyName in myObj.GetType().GetProperties())
{
string n = PropertyName.GetValue(myObj).ToString();
}
}
Then we need to loop the actual properties of the current loop ClassName object.
Then you pass the argument .GetValue (as you are now looping through the properties - the actual properties assigned, not the definition of properties).
After, you still need to specify what object you want the value of. So by passing myObj, you are specifying the ClassName->Property of the current loop of ListName.
EDIT:
List<Notes> myNotesNow = new List<Notes>();
myNotesNow.Add(new Notes
{
note1 = "Valuye"
// adding more properties
});
List<Notes> theseTrips = myNotesNow;
foreach (Notes PaidTrips in theseTrips)
{
foreach (var myVariable in PaidTrips.GetType().GetProperties())
{
string n = myVariable.GetValue(PaidTrips).ToString();
string forBreakPoint = "";
// code to create PDF
}
}
For your question - I guess that ListName is not of type string and so you get the expected behavior (see: https://learn.microsoft.com/en-us/dotnet/api/system.object.tostring?view=net-5.0#the-default-objecttostring-method)
In that case you can override the ToString() function of that object to return whatever you need like this:
public override string ToString()
{
return "whatever you want including class properties";
}
On another note, the general approach to variable naming in C# is camelCase and starts with lower case so I suggest to name your variables propertName instead of PropertyName and listName instead of ListName.
Moreover - naming variables for how they are implemented (ListName) is not a best practice as it binds them together, not allowing flexibility in case implementation changes (that comment is true only if it makes sense, as I dont see all the code)
Cheers

C# - Object List to Dictionary

Is there a way to assign the key values of each object in a List to a Dictionary? I want to access that dictionary later to assign the values to properties of a class.
I fetch some values from a BD with inner joins, then I store the values in a list of objects:
IEnumerable<object> units = _unitDetailsQuery.Execute<object>(out totalRecords);
Each object has these properties:
Brand (string)
Model (string)
Code (string)
BranchId (int)
Then I create a Dictionary
IDictionary<string, object> props = new Dictionary<string, object>();
I've seen in another question something like this (i've adapted the snippet to my code):
BindingFlags bindingAttr = BindingFlags.DeclaredOnly | BindingFlags.Public | BindingFlags.Instance;
foreach (object unit in units)
{
props = unit.GetType().GetProperties(bindingAttr).ToDictionary
(
propInfo => propInfo.Name,
propInfo => propInfo.GetValue(unit, null)
);
}
But props gets no value after the assignment.
Internally the object is something like this:
So how do I get those values? Thanks in advance.
You can use this extension method, which will reflect over the object's public properties and put them into a dictionary.
public static class ObjectExtensionMethods
{
static public Dictionary<string, object> ToPropertyDictionary(this object o)
{
var d = new Dictionary<string, object>();
foreach (var p in o.GetType().GetProperties(BindingFlags.Instance | BindingFlags.Public))
{
d.Add(p.Name, p.GetValue(o));
}
return d;
}
}
You could use this on a list of objects like so:
var listOfDictionaries = listOfObjects.Select( o => o.ToPropertyDictionary() );
Giving credit to #John Wu, but since you said you wanted information to be editable, I think the extension method instead has to return string, System.Reflection.PropertyInfo instead. That way, properties can be updated and selected.
public static Dictionary<string, System.Reflection.PropertyInfo> ToPropertyDictionary(object o)
{
var d = new Dictionary<string, System.Reflection.PropertyInfo>();
foreach (var p in o.GetType().GetProperties(BindingFlags.Instance | BindingFlags.Public))
{
d.Add(p.Name, p);
}
return d;
}
This way, you can get/set the original object's properties. It could no longer be an extension method.
This way, you can edit the object like so. Let's say I have a random object named o with a variable named X that's an integer.
Then, I can get the dictionary.
var dict = ToPropertyDictionary(o);
Setting X with a value of 2 can be done with:
dict("X").SetValue(o, 2);
Getting X then would be done with:
dict("X").GetValue(o)

Simplify loop to get the attributes of a class

I have this two foreach loops to get all the attributes, linearly, of one my class.
foreach (PropertyInfo property in GetType().GetProperties())
{
foreach (Attribute attribute in property.GetCustomAttributes(true))
{
}
}
How I can simplify this two loops to one loop or to linq operation to get the class attributes ?
You can rely on SelectMany()
var attributes = GetType().GetProperties()
.SelectMany(p => p.GetCustomAttributes(true));
foreach (var attribute in attributes)
{
// Code goes here
}
Or using query notation:
var attributes=from p in yourObject.GetType().GetProperties()
from a in p.GetCustomAttributes(true)
select a;

In C#, how do I change a Key-Value-Property's value while recursively traversing an ExpandoObject?

The Problem
Using C#, I need to traverse an object that has been cast to an ExpandoObject from XML and replace any "price" property with a new value.
This object is very unstructured and has many layers of nested nodes (nested ExpandoObjects, actually). More specifically, the hierarchy may look like this:
Product => price, quantity, accessories
Each accessory may have a price and quantity and may itself have accessories, this is why I need recursion.
What I have so far
public ExpandoObject UpdatePricing(ExpandoObject exp)
{
//Is there a price property?
var hasPrice = exp.Any(a => a.Key == "price");
if (hasPrice)
{
//update price here
exp.price = 0; //Some other price
}
//Now loop through the whole object. If any of the properties contain an expando, then call this method again
foreach (var kvp in exp)
{
if (kvp.Value is ExpandoObject)
{
//THIS CODE IS NO GOOD BECAUSE kvp.Value has no setter!!
kvp.Value = UpdatePricing(kvp.Value);
}
}
return exp;
}
The problem I run into is that the kvp.Value has no setter, so I can't run this method recursively.
Any suggestions are appreciated. Thanks!
Since ExpandoObject implements IDictionary<string, Object> things can get a bit easier. We can also change the return type to void because we don't need to reassign the result.
void UpdatePrice(ExpandoObject expando, decimal price)
{
var map = (IDictionary<string, Object>)expando;
if (map.ContainsKey("price"))
map["price"] = price;
foreach (var value in map.Values)
{
if (value is ExpandoObject)
UpdatePrice((ExpandoObject)value, price);
}
}
I don't know much about ExpandoObject. But like most dictionary implementations, I assume that in general if you want your key-value pair to be updated to have a different value, you need to go through the dictionary interface.
Note that you (probably) won't be allowed to modify the dictionary while you're enumerating its contents. So you'll need to build a list of elements to update and do that in a separate operation.
For example:
List<string> keysToUpdate = new List<string>();
foreach (var kvp in exp)
{
if (kvp.Value is ExpandoObject)
{
keysToUpdate.Add(kvp.Key);
}
}
foreach (string key in keysToUpdate)
{
exp[key] = UpdatePricing(exp[key]);
}
You could also keep the whole KeyValuePair value in your list, to avoid the second retrieval of the value, but I'm guessing that's not an important optimization here.
I just ran a little test on this and was able to get it to work by having the expando be dynamic:
public static ExpandoObject DoWork(ExpandoObject obj)
{
dynamic expando = obj;
//update price
if (obj.Any(c => c.Key == "price"))
expando.price = 354.11D;
foreach (var item in expando)
{
if (item.Value is ExpandoObject)
{
//call recursively
DoWork(item.Value);
}
}
return expando;
}
it elimitates type safety, but it looks like you don't have that luxury anyways, dynamic is the best way to interact with expandos in fact according to MSDN:
"In C#, to enable late binding for an instance of the ExpandoObject
class, you must use the dynamic keyword. For more information, see
Using Type dynamic (C# Programming Guide)."
this means that if you don't use the dynamic keyword, you are running the Expando in the CLR instead of the DLR which will have some odd consequences like not being able to set values. Hopefully this helps.

Accessing a Collection Through Reflection

Is there a way to iterate (through foreach preferably) over a collection using reflection? I'm iterating over the properties in an object using reflection, and when the program gets to a type that is a collection, I'd like it to iterate over the contents of the collection and be able to access the objects in the collection.
At the moment I have an attribute set on all of my properties, with an IsCollection flag set to true on the properties that are collections. My code checks for this flag and if it's true, it gets the Type using reflection. Is there a way to invoke GetEnumerator or Items somehow on a collection to be able to iterate over the items?
I had this issue, but instead of using reflection, i ended up just checking if it was IEnumerable. All collections implement that.
if (item is IEnumerable)
{
foreach (object o in (item as IEnumerable))
{
}
} else {
// reflect over item
}
I've tried to use a similar technique as Darren suggested, however just beware that not just collections implement IEnumerable. string for instance is also IEnumerable and will iterate over the characters.
Here's a small function I'm using to determine if an object is a collection (which will be enumerable as well since ICollection is also IEnumerable).
public bool isCollection(object o)
{
return typeof(ICollection).IsAssignableFrom(o.GetType())
|| typeof(ICollection<>).IsAssignableFrom(o.GetType());
}
Just get the value of the property and then cast it into an IEnumerable. Here is some (untested) code to give you an idea:
ClassWithListProperty obj = new ClassWithListProperty();
obj.List.Add(1);
obj.List.Add(2);
obj.List.Add(3);
Type type = obj.GetType();
PropertyInfo listProperty = type.GetProperty("List", BindingFlags.Public);
IEnumerable listObject = (IEnumerable) listProperty.GetValue(obj, null);
foreach (int i in listObject)
Console.Write(i); // should print out 123
Just for information may be it will be of someone's help...
I had a class with nested classes and collection of some other classes. I wanted to save the property values of the class as well nested classes and collection of classes. My code is as follows:
public void LogObject(object obj, int indent)
{
if (obj == null) return;
string indentString = new string(' ', indent);
Type objType = obj.GetType();
PropertyInfo[] properties = objType.GetProperties();
foreach (PropertyInfo property in properties)
{
Type tColl = typeof(ICollection<>);
Type t = property.PropertyType;
string name = property.Name;
object propValue = property.GetValue(obj, null);
//check for nested classes as properties
if (property.PropertyType.Assembly == objType.Assembly)
{
string _result = string.Format("{0}{1}:", indentString, property.Name);
log.Info(_result);
LogObject(propValue, indent + 2);
}
else
{
string _result = string.Format("{0}{1}: {2}", indentString, property.Name, propValue);
log.Info(_result);
}
//check for collection
if (t.IsGenericType && tColl.IsAssignableFrom(t.GetGenericTypeDefinition()) ||
t.GetInterfaces().Any(x => x.IsGenericType && x.GetGenericTypeDefinition() == tColl))
{
//var get = property.GetGetMethod();
IEnumerable listObject = (IEnumerable)property.GetValue(obj, null);
if (listObject != null)
{
foreach (object o in listObject)
{
LogObject(o, indent + 2);
}
}
}
}
}
An called this function
LogObject(obj, 0);
However, I have some structs inside my classes and I need to figure out how to get their values. Moreoevr, I have some LIst. I need to get their value as well.... I will post if I update my code.
The best you could probably do would be to check if the object implements certain collection interfaces - probably IEnumerable would be all that you need. Then it's just a matter of calling GetEnumerator() off of the object, and using IEnumerator.MoveNext() and IEnumerator.Current to work your way through the collection.
This won't help you if the collection doesn't implement those interfaces, but if that's the case it's not really much of a collection, I suppose.
When your using reflection you aren't necessarily using an instance of that object. You would have to create an instance of that type of be able to iterate through the object's properties. So if you are using reflection use the ConstructorInfo.Invoke() (?) method to create a new instance or point to an instance of the type.
I would look at the Type.FindInterfaces method. This can filter out the interfaces implemented by a given type. As in PropertyInfo.PropertyType.FindInterfaces(filterMethod, filterObjects). You can filter by IEnumerable and see if any results are returned. MSDN has a great example in the method documentation.
If you're not using an instance of the object but rather a Type, you can use the following:
// type is IEnumerable
if (type.GetInterface("IEnumerable") != null)
{
}
A rather straightforward approach would be to type cast the object as the collection and directly use that.

Categories

Resources