I have to run comparisons between thousands of pairs of objects, and then perform actions depending on the differences.
Is there an "accepted" way of doing this?
class ObjectA
{
public string FieldA { get; set; }
public string FieldB { get; set; }
public string FieldC { get; set; }
}
class ObjectB
{
public string FieldA { get; set; }
public string FieldB { get; set; }
public string FieldC { get; set; }
public bool Equals(ObjectA obj)
{
if ((object)obj == null) return false;
if (this.FieldA != obj.FieldA) return false;
if (this.FieldB != obj.FieldB) return false;
if (this.FieldC != obj.FieldC) return false;
return true;
}
}
void test()
{
ObjectA a = new ObjectA();
ObjectB b = new ObjectB();
if (b.Equals(a))
{
Console.WriteLine("Same!!");
}
}
That does a fairly simple test to determine if b=a, but I also want to know what is different between them.
Should I add a differences() method that returns a list of properties? That seems a bit not.net though, as then I'll be bandying about strings.
public List<string> Differences(ObjectA obj)
{
List<string> differences = new List<string>();
if ((object)obj == null)
{
differences.Add("null");
}
else
{
if (this.FieldA != obj.FieldA) differences.Add("FieldA");
if (this.FieldB != obj.FieldB) differences.Add("FieldB");
if (this.FieldC != obj.FieldC) differences.Add("FieldC");
}
return differences;
}
Also that seems much slower than the first, as I would be creating all those List<string>, and not short-cutting the comparisons. Or is that just the price I pay for having the extra information?
Maybe you should try this:
http://comparenetobjects.codeplex.com/
All credit to the author...
Edit: Since codeplex is shutting down, the github url : https://github.com/GregFinzer/Compare-Net-Objects
There is nothing built in that will allow you to represent partial objects (i.e the differences).
Your approach seems reasonable for what you are trying to achieve.
using System;
using System.Collections.Generic;
using System.Linq;
using System.Text;
class ObjectA
{
public string PropertyA { get; set; }
public string PropertyB { get; set; }
public string PropertyC { get; set; }
public DateTime PropertyD { get; set; }
public string FieldA;
public DateTime FieldB;
}
class ObjectB
{
public string PropertyA { get; set; }
public string PropertyB { get; set; }
public string PropertyC { get; set; }
public DateTime PropertyD { get; set; }
public string FieldA;
public DateTime FieldB;
}
class Program
{
static void Main(string[] args)
{
// create two objects with same properties
ObjectA a = new ObjectA() { PropertyA = "test", PropertyB = "test2", PropertyC = "test3" };
ObjectB b = new ObjectB() { PropertyA = "test", PropertyB = "test2", PropertyC = "test3" };
// add fields to those objects
a.FieldA = "hello";
b.FieldA = "Something differnt";
if (a.ComparePropertiesTo(b))
{
Console.WriteLine("objects have the same properties");
}
else
{
Console.WriteLine("objects have diferent properties!");
}
if (a.CompareFieldsTo(b))
{
Console.WriteLine("objects have the same Fields");
}
else
{
Console.WriteLine("objects have diferent Fields!");
}
Console.Read();
}
}
public static class Utilities
{
public static bool ComparePropertiesTo(this Object a, Object b)
{
System.Reflection.PropertyInfo[] properties = a.GetType().GetProperties(); // get all the properties of object a
foreach (var property in properties)
{
var propertyName = property.Name;
var aValue = a.GetType().GetProperty(propertyName).GetValue(a, null);
object bValue;
try // try to get the same property from object b. maybe that property does
// not exist!
{
bValue = b.GetType().GetProperty(propertyName).GetValue(b, null);
}
catch
{
return false;
}
if (aValue == null && bValue == null)
continue;
if (aValue == null && bValue != null)
return false;
if (aValue != null && bValue == null)
return false;
// if properties do not match return false
if (aValue.GetHashCode() != bValue.GetHashCode())
{
return false;
}
}
return true;
}
public static bool CompareFieldsTo(this Object a, Object b)
{
System.Reflection.FieldInfo[] fields = a.GetType().GetFields(); // get all the properties of object a
foreach (var field in fields)
{
var fieldName = field.Name;
var aValue = a.GetType().GetField(fieldName).GetValue(a);
object bValue;
try // try to get the same property from object b. maybe that property does
// not exist!
{
bValue = b.GetType().GetField(fieldName).GetValue(b);
}
catch
{
return false;
}
if (aValue == null && bValue == null)
continue;
if (aValue == null && bValue != null)
return false;
if (aValue != null && bValue == null)
return false;
// if properties do not match return false
if (aValue.GetHashCode() != bValue.GetHashCode())
{
return false;
}
}
return true;
}
}
As long as the names of the properties are equal, you can do this using the property map of each object and linq. I've done this in the past, but I don't have the code in front of me at the moment, sorry.
Here's an example I used for unit testing two instances of the same object type. I was testing to ensure that the properties that were serialized to file and populated in a new instance of the same object type were the same. Note that this is using System.Reflection and that you are comparing instances of the same type.
//Assume yourobjectA and yourobjectB have already been instantiated and populated.
//loop throught he properties and compare
//they should all be set the same as the previous instance
PropertyInfo[] propertiesA = yourobjectA.GetType().GetProperties(BindingFlags.Public | BindingFlags.Instance);
PropertyInfo[] propertiesB = yourobjectB.GetType().GetProperties(BindingFlags.Public | BindingFlags.Instance);
int count = oldProperties.Length;
for (int i = 0; i < count; i++)
{
if ((propertiesA [i].CanRead) && (propertiesB [i].CanRead))
{
if (propertiesA [i].PropertyType == typeof(String))
{
object oldStringValue = (string)propertiesA[i].GetValue(yourobjectA, null);
object newStringValue = (string)propertiesB[i].GetValue(yourobjectB., null);
if(oldStringValue != newStringValue )
{
//Do something
}
}
if (propertiesA [i].PropertyType == typeof(Boolean))
{
object oldBoolValue = (bool)propertiesA [i].GetValue(yourobjectA, null);
object newBoolValue = (bool)propertiesB [i].GetValue(yourobjectB., null);
if(oldBoolValue != newBoolValue)
{
//Do something
}
}
}
}
Related
I have the following model that has these fields:
[Key]
public string Id { get; set; }
[IsSearchable]
public string Code{ get; set; }
[IsSearchable]
public string Name { get; set; }
[IsSearchable]
public string Address { get; set; }
[IsSearchable]
public string PostCode { get; set; }
[IsFilterable]
public int? Setting{ get; set; }
[IsFilterable, IsSortable]
public Location Location { get; set; }
I am writing a method to compare whether values in a database match this model. So far it looks like this:
private bool CompareEquality(Index resultBody, Type indexType)
{
var properties = indexType.GetProperties(BindingFlags.Instance | BindingFlags.Public);
List<PropertyInfo> searchableProperties = new List<PropertyInfo>();
List<PropertyInfo> filterableProperties = new List<PropertyInfo>();
List<PropertyInfo> sortableProperties = new List<PropertyInfo>();
if (properties.Count() == resultBody.Fields.Count)
{
foreach (var property in properties)
{
var isSearchableAttribute = property.GetCustomAttribute<IsSearchableAttribute>();
var isFilterableAttribute = property.GetCustomAttribute<IsFilterableAttribute>();
var isSortableAttribute = property.GetCustomAttribute<IsSortableAttribute>();
if (isSearchableAttribute != null)
{
searchableProperties.Add(property);
}
if (isFilterableAttribute != null)
{
filterableProperties.Add(property);
}
if (isSortableAttribute != null)
{
sortableProperties.Add(property);
}
}
CheckAttributeEquality(searchableProperties, filterableProperties, sortableProperties);
}
return false;
}
The CheckAttributeEquality method:
private bool CheckAttributeEquality(List<PropertyInfo> searchableProperties, List<PropertyInfo> filterableProperties, List<PropertyInfo> sortableProperties)
{
if (searchableProperties.Count == 4 && filterableProperties.Count == 2 && sortableProperties.Count == 1)
{
CheckPropertyFields(searchableProperties, filterableProperties, sortableProperties);
return true;
}
return false;
}
As I started to write a method to check that the field names match, like so:
foreach (var property in searchableProperties)
{
if (property.Name == "Id" ||)
{
...
}
if (property.Name == "Code")
{
...
}
// etc
I realised how messy and long-winded this whole approach is. I am not hugely experienced in C# and would appreciate any advice as to how I can refactor this up a little bit? I want to check for attribute and name matches.
you could use the Typedescriptor (using System.ComponentModel) for that. Try this:
var pdc = TypeDescriptor.GetProperties( this ); //or your model object, if its not "this"
foreach (var property in searchableProperties)
{
var descriptors = pdc[ property.Name ];
// check if your searchable descriptor is there, and do error handling
}
Once it works, you could also try to solve it with LINQ.
I successfully implemented the answer on this question. However, it does not work when I have a List. When I ran this through, here's what I got back:
AccountDataResult: 'AccountFound'
AccountList: 'CustomWebService.TestApplication.ServiceMethods.CustomClassOutputData+DataList[]'
This list has X items inside and I need to list out all of the properties for each one. What is the best method to achieve this?
Here is what I have today:
void AddPropertiesToTextbox(object output, TextBox txt)
{
PropertyInfo[] piData = null;
piData = Utility.GetPublicProperties(output.GetType());
foreach (PropertyInfo pi in piData)
{
if (pi.Name.ToLower() != "extensiondata")
{
textResult.Text += string.Format("{0}: '{1}'", pi.Name, pi.GetValue(output, null));
textResult.Text += Environment.NewLine;
}
}
}
Here is my web service Model:
[DataContract]
public class OutputData
{
[DataContract]
public class AccountData
{
[DataMember]
public string AccountStatus { get; set; }
[DataMember]
public string FirstName { get; set; }
[DataMember]
public string LastName { get; set; }
}
[DataContract]
public enum AccountDataResults
{
[EnumMember]
None,
[EnumMember]
AccountFound,
[EnumMember]
NoAccounts
}
[DataMember]
public List<AccountData> DataList { get; set; }
[DataMember]
public AccountDataResults AccountDataResult { get; set; }
}
You can check the PropertyInfo's PropertyType type definition by calling GetGenericTypeDefinition()
if(pi.PropertyType.GetGenericTypeDefinition() == typeof(List<>))
{
// recurse through properties
}
Demo:
class Program
{
static void Main(string[] args)
{
object test = new Test { DemoProperty = "Some", DemoListProperty = new List<int> { 1, 2, 3, 4 } };
Type type = typeof(Test);
foreach (var pi in type.GetProperties())
{
if (pi.PropertyType.IsGenericType && pi.PropertyType.GetGenericTypeDefinition() == typeof(List<>))
{
IEnumerable objects = pi.GetGetMethod().Invoke(test,null) as IEnumerable;
foreach (var o in objects)
{
Console.WriteLine(o); //may want to do recursion here and iterate over these properties too
}
}
}
}
}
class Test
{
public string DemoProperty { get; set; }
public List<int> DemoListProperty { get; set; }
}
In order to get this working I had to scrap my initial setup. Here is what I am doing now. This works PERFECTLY for me! This was used to pull all properties i care about into a result list.
Get all properties for any type:
public static PropertyInfo[] GetPublicProperties2(this Type type)
{
return type.GetProperties(BindingFlags.Public | BindingFlags.Instance);
}
I then have this method that is generic for any property which calls itself when there is an array.
void AddPropertiesToTextbox(object output, string rootNamespace = "")
{
PropertyInfo[] piData = null;
piData = Utility.GetPublicProperties2(output.GetType());
foreach (PropertyInfo pi in piData)
{
if (pi.PropertyType.IsArray == true)
{
Array subOutput = (Array)pi.GetValue(output);
for (int i = 0; i < subOutput.Length; i++)
{
textResult.Text += string.Format("{0}------------------------{0}", Environment.NewLine);
object o = subOutput.GetValue(i);
AddPropertiesToTextbox(o, pi.Name);
}
}
else
{
if (pi.Name.ToLower() != "extensiondata")
{
if (string.IsNullOrWhiteSpace(rootNamespace) == false) {
textResult.Text += string.Format("{2}.{0}: '{1}'", pi.Name, pi.GetValue(output, null), rootNamespace);
textResult.Text += Environment.NewLine;
} else {
textResult.Text += string.Format("{0}: '{1}'", pi.Name, pi.GetValue(output, null));
textResult.Text += Environment.NewLine;
}
}
}
}
}
So i have an object filled with many inner objects. To get a value of inner object that i want to know i must first check if all parents all the way to root object are not null. Code looks like this:
StringBuilder stringBuilder = new StringBuilder();
//Wanted object can be even deeper
if (root != null &&
root.InnerObject1 != null &&
root.InnerObject1.InnerObject2 != null &&
root.InnerObject1.InnerObject2.InnerObject3 != null &&
root.InnerObject1.InnerObject2.InnerObject3.value != null)
{
stringBuilder.Append(root.InnerObject1.InnerObject2.InnerObject3.value)
}
Can this be done in more elegant way?
With current C# version: no.
With the next one: yes.
C# 6 will have a new feature called null-conditional operator designed for cases like this. It will let you write
var value = root?.InnerObject2?.InnerObject2?.InnerObject3?.value;
if(value != null)
stringBuilder.Append(value);
Why not introduce:
private static bool NestedElementAvailable(Node startNode, int depth)
{
Node node = startNode
while(node != null && depth > 0)
{
node = node.InnerNode;
depth--;
}
return depth == 0 && node != null && node.Value != null;
}
Edit:
private static bool SomeSeriousNameForCondition(Node node)
{
return //the condition
}
Then in the orignial code
if(SomeSeriousNameForCondition(root))
I know this does not reduce the code but at least its more readable.
Consider this:
class Program
{
static void Main(string[] args)
{
Test root = new Test
{
Property1 = new Test { Value = 3 },
Property2 = new Test { Value = 4 },
Property3 = new Test { Property1 = new Test { Value = 4} }
};
int testValue1;
if (root.TryGetData<int>(out testValue1,"Property1","Value"))
{
Console.WriteLine(testValue1);
}
int testValue2;
if (root.TryGetData<int>(out testValue2, "Property2","Property1","Value" ))
{
Console.WriteLine("Would be bad if this would enter here");
}
if (root.TryGetData<int>(out testValue2, "Property3", "Property1", "Value"))
{
Console.WriteLine(testValue2);
}
}
}
class Test
{
public Test Property1 { get; set; }
public Test Property2 { get; set; }
public Test Property3 { get; set; }
public int Value { get; set; }
}
public static class ExtensionMethods
{
public static bool TryGetData<T>(this object theObject, out T value, params string[] path)
{
object cursor = theObject;
for (int i = 0; i < path.Length; i++)
{
if (cursor == null)
{
value = default(T);
return false;
}
Type t = cursor.GetType();
cursor = t.GetProperty(path[i]).GetValue(cursor);
}
value = (T)cursor;
return true;
}
}
It's by no means good practise but it would get the job done.
I'm trying to do some validation in a WCF service, and for that I'm using WCFDataAnnotations which I found through this post
Problem is that it doesn't validate recursively, so for a nested object it doesn't work. Let's say this
[DataContract]
public class Model
{
[DataMember]
[Required(ErrorMessage = "RequiredOne is required")]
public string RequiredOne { get; set; }
[DataMember]
[StringLength(10, ErrorMessage = "Not Required should be at most 10 characters long")]
public string NotRequired { get; set; }
[DataMember]
[Required(ErrorMessage = "ChildModel is required")]
public ChildModel ChildModel { get; set; }
}
[DataContract]
public class ChildModel
{
[DataMember]
[Required(ErrorMessage = "RequiredValue is required")]
public string RequiredValue { get; set; }
[DataMember]
public string NotRequiredValue { get; set; }
}
It won't get the childModel RequiredValue as precisely that, required.
So I was taking a look into the source code of that dll and trying to make it work. The actual code is
public class DataAnnotationsObjectValidator : IObjectValidator
{
public IEnumerable<ValidationResult> Validate(object input)
{
if (input == null) return Enumerable.Empty<ValidationResult>();
return from property in TypeDescriptor.GetProperties(input).Cast<PropertyDescriptor>()
from attribute in property.Attributes.OfType<ValidationAttribute>()
where !attribute.IsValid(property.GetValue(input))
select new ValidationResult
(
attribute.FormatErrorMessage(string.Empty),
new[] { property.Name }
);
}
}
So my thought are changing this to something like this
public IEnumerable<ValidationResult> Validate(object input)
{
if (input == null) return Enumerable.Empty<ValidationResult>();
var validationResults = new List<ValidationResult>();
foreach (var prop in TypeDescriptor.GetProperties(input).Cast<PropertyDescriptor>())
{
foreach (var att in prop.Attributes.OfType<ValidationAttribute>())
{
//This doesn't work, it's one of the several
//attempts I've made
if (prop.ComponentType.IsClass)
Validate(prop.ComponentType);
if (!att.IsValid(prop.GetValue(input)))
{
validationResults.Add(new ValidationResult(
att.FormatErrorMessage(string.Empty),
new[] { prop.Name }
));
}
}
}
return validationResults;
}
The intention is check if any of the properties is a complex one and if it's the case validate itself recursively, but I'm not sure how to check that given the "props" are casted to TypeDescriptors.
Thanks
Seems to me that the following code should do the trick:
public IEnumerable<ValidationResult> Validate(object input)
{
return ValidateWithState(input, new HashSet<object>());
}
private IEnumerable<ValidationResult> ValidateWithState(object input, HashSet<object> traversedInputs)
{
if (input == null || traversedInputs.Contains(input))
{
return Enumerable.Empty<ValidationResult>();
}
var validationResults = new List<ValidationResult>();
foreach (var prop in TypeDescriptor.GetProperties(input).Cast<PropertyDescriptor>())
{
foreach (var att in prop.Attributes.OfType<ValidationAttribute>())
{
if (!att.IsValid(prop.GetValue(input)))
{
validationResults.Add(new ValidationResult(
att.FormatErrorMessage(string.Empty),
new[] { prop.Name }
));
}
traversedInputs.Add(input);
if (prop.PropertyType.IsClass || prop.PropertyType.IsInterface))
{
validationResults.AddRange(ValidateWithState(prop.GetValue(input), traversedInputs));
}
}
return validationResults;
}
Might not be the most elegant solution, but i think it'll work.
Now I am able to validate public List ChildModel.
With Reference to DevTrends.WCFDataAnnotations, ValidatingParameterInspector.cs class,
(http://wcfdataannotations.codeplex.com/SourceControl/latest#DevTrends.WCFDataAnnotations/ValidatingParameterInspector.cs).
I am sure ValidateCollection can be further modified to check collection within ChildModel.Currently it check upto one level only .
My Example,
[DataContract]
public class Model
{
[DataMember]
[Required(ErrorMessage = "RequiredOne is required")]
public string RequiredOne { get; set; }
[DataMember]
[StringLength(10, ErrorMessage = "Not Required should be at most 10 characters long")]
public string NotRequired { get; set; }
[DataMember]
[Required(ErrorMessage = "ChildModel is required")]
public List<ChildModel> ChildModel { get; set; }
}
Original code do not validate List,So I created one more function ValidateCollection
Manipulated the object[] inputs to extract each ChildModel class and put it back in object[] inputs like model class reside in object[] inputs.
public object BeforeCall(string operationName, object[] inputs)
{
var validationResults = new List<ValidationResult>(); ErrorMessageGenerator.isValidationFail = false;
ErrorMessageGenerator.ErrorMessage = string.Empty;
***inputs=ValidateCollection( operationName, inputs);***
foreach (var input in inputs)
{
foreach (var validator in _validators)
{
var results = validator.Validate(input);
validationResults.AddRange(results);
}
}
if (validationResults.Count > 0)
{
return _errorMessageGenerator.GenerateErrorMessage(operationName, validationResults);
}
return null;
}
private object[] ValidateCollection(string operationName, object[] inputs)
{
object[] inputs1 = inputs;
try
{
foreach (var input in inputs)
{
foreach (var property in input.GetType().GetProperties())
{
IEnumerable enumerable = null;
if (property.PropertyType.Name.Contains("List"))
{
enumerable = property.GetValue(input, null) as IEnumerable;
int j = 0;
object[] o1 = new object[inputs.Count() + enumerable.OfType<object>().Count()];
for (int k = 0; k < inputs.Count(); k++)
{
o1[k] = inputs[k];
}
foreach (var item in enumerable)
{
o1[inputs.Count() + j] = item;
j = j + 1;
if (j == (o1.Length - inputs.Count()))
inputs = o1;
}
}
}
}
return inputs;
}
catch
{
return inputs1;
}
}
I have a class, which is created and populated from an xml string, I've simplified it for example purposes:
[XmlRoot("Person")]
public sealed class Person
{
[XmlElement("Name")]
public string Name { get; set; }
[XmlElement("Location")]
public string Location { get; set; }
[XmlElement("Emails", Type = typeof(PersonEmails)]
public PersonEmails Emails { get; set; }
}
public class PersonEmails
{
[XmlElement("Email", Type = typeof(PersonEmail))]
public PersonEmail[] Emails { get; set; }
}
public class PersonEmail
{
[XmlAttribute("Type")]
public string Type { get; set; }
[XmlText]
public string Value { get; set; }
}
To extract the information, I'm trying to load them into another class, which is simply:
public class TransferObject
{
public string Name { get; set; }
public ObjectField[] Fields { get; set; }
}
public class ObjectField
{
public string Name { get; set; }
public string Value { get; set; }
}
I'm only populating "Fields" from the other object, which would simply be (Name = "Location", Value = "London"), but for Emails, (Name = "Email"+Type, Value = jeff#here.com)
Currently I can populate all the other fields, but I'm stuck with Emails, and knowing how to dig deep enough to be able to use reflection (or not) to get the information I need. Currently I'm using:
Person person = Person.FromXmlString(xmlString);
List<ObjectField> fields = new List<ObjectField>();
foreach (PropertyInfo pinfo in person.getType().GetProperties()
{
fields.Add(new ObjectField { Name = pinfo.Name, Value = pinfo.getValue(person, null).ToString();
}
How can I expand on the above to add all my emails to the list?
You are trying to type cast a complex values type to string value so you lost the data. Instead use following code:
class Program
{
static void Main(string[] args)
{
Person person = new Person();
person.Name = "Person One";
person.Location = "India";
person.Emails = new PersonEmails();
person.Phones = new PersonPhones();
person.Emails.Emails = new PersonEmail[] { new PersonEmail() { Type = "Official", Value = "xyz#official.com" }, new PersonEmail() { Type = "Personal", Value = "xyz#personal.com" } };
person.Phones.Phones = new PersonPhone[] { new PersonPhone() { Type = "Official", Value = "789-456-1230" }, new PersonPhone() { Type = "Personal", Value = "123-456-7890" } };
List<ObjectField> fields = new List<ObjectField>();
fields = GetPropertyValues(person);
}
static List<ObjectField> GetPropertyValues(object obj)
{
List<ObjectField> propList = new List<ObjectField>();
foreach (PropertyInfo pinfo in obj.GetType().GetProperties())
{
var value = pinfo.GetValue(obj, null);
if (pinfo.PropertyType.IsArray)
{
var arr = value as object[];
for (var i = 0; i < arr.Length; i++)
{
if (arr[i].GetType().IsPrimitive)
{
propList.Add(new ObjectField() { Name = pinfo.Name + i.ToString(), Value = arr[i].ToString() });
}
else
{
var lst = GetPropertyValues(arr[i]);
if (lst != null && lst.Count > 0)
propList.AddRange(lst);
}
}
}
else
{
if (pinfo.PropertyType.IsPrimitive || value.GetType() == typeof(string))
{
propList.Add(new ObjectField() { Name = pinfo.Name, Value = value.ToString() });
}
else
{
var lst = GetPropertyValues(value);
if (lst != null && lst.Count > 0)
propList.AddRange(lst);
}
}
}
return propList;
}
}
Check this snippet out:
if(pinfo.PropertyType.IsArray)
{
// Grab the actual instance of the array.
// We'll have to use it in a few spots.
var array = pinfo.GetValue(personObject);
// Get the length of the array and build an indexArray.
int length = (int)pinfo.PropertyType.GetProperty("Length").GetValue(array);
// Get the "GetValue" method so we can extact the array values
var getValue = findGetValue(pinfo.PropertyType);
// Cycle through each index and use our "getValue" to fetch the value from the array.
for(int i=0; i<length; i++)
fields.Add(new ObjectField { Name = pinfo.Name, Value = getValue.Invoke(array, new object[]{i}).ToString();
}
// Looks for the "GetValue(int index)" MethodInfo.
private static System.Reflection.MethodInfo findGetValue(Type t)
{
return (from mi in t.GetMethods()
where mi.Name == "GetValue"
let parms = mi.GetParameters()
where parms.Length == 1
from p in parms
where p.ParameterType == typeof(int)
select mi).First();
}
You can definately do it with Reflection... You can take advantage of the fact that a Type can tell you if it's an array or not (IsArray)... and then take advantage of the fact that an Array has a method GetValue(int index) that will give you a value back.
Per your comment
Because Emails is a property within a different class, recursion should be used. However the trick is knowing when to go to the next level. Really that is up to you, but
if it were me, I would use some sort of Attribute:
static void fetchProperties(Object instance, List<ObjectField> fields)
{
foreach(var pinfo in instance.GetType().GetProperties())
{
if(pinfo.PropertyType.IsArray)
{
... // Code described above
}
else if(pinfo.PropertyType.GetCustomAttributes(typeof(SomeAttribute), false).Any())
// Go the next level
fetchProperties(pinfo.GetValue(instance), fields);
else
{
... // Do normal code
}
}
}