First element foreach not showing c# - c#

I have an empty List (objPassenger) with passengers spaces incoming, and another list with data of passengers confirmed (objFilePassenger). I can't start from the 0 index in foreach.
Demo in web
foreach (var filePassenger in objFilePassengers)
{
//TypeId == 1 is ADULT
//TypeId == 2 is CHILD
var passenger = objPassengers.FirstOrDefault(p => (
(p.TypeId == filePassenger.Type && p.TypeId == "1") || (p.TypeId != "1" && filePassenger.Type != "1" && p.Age == filePassenger.Age)));
if (passenger != null)
{
passenger.PassengerId = filePassenger.PassengerID;
passenger.TypeId = filePassenger.Type;
passenger.FirstName = filePassenger.Name.ToTitleCase();
passenger.LastName = filePassenger.LastName.ToTitleCase();
passenger.Age = filePassenger.Age;
}
}
How can I skip the object if p.TypeId== 1 && filePassenger.Type == 2? This deferred object I need to skip and go next, until the type is equal like TypeId == 2 and Type == 2 (CHILD).

Updated Answer
Have you considered using a custom Collection to hold your passengers? You can build all the logic into the collection and make it enumerable to iterate through the passengers.
Here's a basic example built from your code:
Two data objects to represent a passenger. I use records because they make equality checks and cloning easy, and you get immutability. Just use the class object when you want to edit the record.
public record PassengerRecord(Guid PassengerId, string PassengerName, int PassengerType);
public class Passenger
{
public Guid PassengerId { get; set; } = Guid.NewGuid();
public string PassengerName { get; set; } = "Not Set";
public int PassengerType { get; set; }
public PassengerRecord AsRecord => new(PassengerId, PassengerName, PassengerType);
public Passenger() { }
public Passenger(PassengerRecord record)
{
this.PassengerId = record.PassengerId;
this.PassengerName = record.PassengerName;
this.PassengerType = record.PassengerType;
}
}
And then a very basic PassengerList which implements IEnumerable, restricts the number of passengers and checks if they are already on the list. Build your own logic and functionality into this.
public class PassengerList : IEnumerable<PassengerRecord>
{
private List<PassengerRecord> _items = new List<PassengerRecord>();
private int _maxRecords;
public PassengerList(int maxRecords)
=> _maxRecords = maxRecords;
public bool TryAddItem(Passenger passenger)
=> addItem(passenger.AsRecord);
public bool TryAddItem(PassengerRecord passenger)
=> addItem(passenger);
public bool addItem(PassengerRecord passenger)
{
// Doi whatever checks you want to do
if (!_items.AsQueryable().Any(item => item == passenger) && _items.Count < _maxRecords)
_items.Add(passenger);
return true;
}
public PassengerRecord this[int index]
=> _items[index];
public IEnumerator<PassengerRecord> GetEnumerator()
=> _items.GetEnumerator();
IEnumerator IEnumerable.GetEnumerator()
=> this.GetEnumerator();
}

This is how I solved it. The issue was 2 lists, one with the requested places, and another with the pre-loaded passengers. The first list could contain, for example, 6 places (3 adults and 3 minors) and the second list could have 2 adult passengers and 2 minor passengers. To skip the third adult (or the amount that was), as there is no others adults and the incomming passanger is a minor, it must be left blank. I solved it as follows.
var count = 0;
for (int i = 0; i < objFilePassengers.Count; i++)
{
if ((objFilePassengers[i].Type == "1" && objPassengers[i].TypeId == "1") || (objPassengers[i].TypeId == "2" && objFilePassengers[i].Type == "2" && objFilePassengers[i].Age < 18))
{
objPassengers[i].PassengerId = objFilePassengers[i].PassengerID;
objPassengers[i].TypeId = objFilePassengers[i].Type;
objPassengers[i].FirstName = objFilePassengers[i].Name.ToTitleCase();
objPassengers[i].LastName = objFilePassengers[i].LastName.ToTitleCase();
objPassengers[i].Age = objFilePassengers[i].Age;
}
else if ((objPassengers[i].TypeId == "1" && objFilePassengers[i].Type == "2"))
{
objFilePassengers.Insert(count + 1, new clsEnix_Passenger
{
PassengerID = objPassengers[i].PassengerId,
Name = "",
LastName = "",
});
count++;
}
}
Example result

Related

How to filter ObservableCollection on multplie values

I have an observable collection.
The class of this collection has several fields which in turn can have server values.
I wish my user to be able to search on numerous fields and values.
I have got as far as using an ICollection but all the examples I have seen is based on searching on 1 field.
This is my class:
public class Tasks
{
private string JobPriority;
private string JobLabel;
private string JobType;
private string UserRef;
}
JobPriority can be 'High', 'Low'
JobLabel can be 'Apple', 'Android', 'Microsoft'
JobType can be 'Internal', 'Private', 'Public'
So, I instantiate my collection as such:
public ObservableCollection<KanbanModelExtra> Tasks
{
get
{
if (_tasks == null)
{
TaskView = CollectionViewSource.GetDefaultView(_tasks);
TaskView.Filter = TaskFilter;
}
return _tasks;
}
set
{
_tasks = value;
}
}
public ICollectionView TaskView { get; private set; }
and set my filter as such:
private bool TaskFilter(
object item )
{
var task = item as KanbanModelExtra;
if ( task == null )
{
return false;
}
foreach ( var searchItem1 in JobPrioritiesToSearchOn )
{
if ( task.JobPriority == searchItem1 )
{
foreach ( var searchItem2 in JobLabelsToSearchOn )
{
if ( task.JobLabel == searchItem2)
{
foreach (var searchItem3 in JobTypesToSearchOn)
{
if (task.JobType == searchItem3)
{
if ( SearchByCurrentUser )
{
if ( task.UserRef == HeartBeat.CurrentUser.UserRef )
{
return true;
}
}
else
{
return true;
}
}
}
}
}
}
}
return false;
}
but this seems 'heavy' and ugly. Also, every change will refresh the collection. Where as I would lie to fresh the collection after all criteria has been choosen.
Is there a better way?
Chain the tests together.
var result = JobPrioritiesToSearchOn.Any(x => x == task.JobPriority);
result = result && JobLabelsToSearchOn.Any(x => x == task.JobLabel);
result = result && JobTypesToSearchOn.Any(x => x == task.JobType);
if (SearchByCurrentUser)
{
result = result && task.UserRef == HeartBeat.CurrentUser.UserRef;
}
return result;
Any() will return true if any of the collection match the filter passed in. In plain English, the first line evaluates to true if any of the JobPrioritiesToSearchOn items are equal to task.JobPriority.
foreach madness can be replaced by this expression:
return ((SearchByCurrentUser && task.UserRef == HeartBeat.CurrentUser.UserRef) || !SearchByCurrentUser) &&
JobPrioritiesToSearchOn.Contains(task.JobPriority) &&
JobLabelsToSearchOn.Contains(task.JobLabel) &&
JobTypesToSearchOn.Contains(task.JobType));

c# Stabilise complicated sort [duplicate]

I want an alphabetic sort with one exception.
There is a Group with a Name = "Public" and an ID = "0" that I want first.
(would rather use ID = 0)
After that then sort the rest by Name.
This does not return public first.
public IEnumerable<GroupAuthority> GroupAuthoritysSorted
{
get
{
return GroupAuthoritys.OrderBy(x => x.Group.Name);
}
}
What I want is:
return GroupAuthoritys.Where(x => x.ID == 0)
UNION
GroupAuthoritys.Where(x => x.ID > 0).OrderBy(x => x.Group.Name);
GroupAuthority has a public property Group and Group has Public properties ID and Name.
I used basically the accepted answer
using System.ComponentModel;
namespace SortCustom
{
public partial class MainWindow : Window
{
public MainWindow()
{
InitializeComponent();
TestSort();
}
private void TestSort()
{
List<CustomSort> LCS = new List<CustomSort>();
LCS.Add(new CustomSort(5, "sss"));
LCS.Add(new CustomSort(6, "xxx"));
LCS.Add(new CustomSort(4, "xxx"));
LCS.Add(new CustomSort(3, "aaa"));
LCS.Add(new CustomSort(7, "bbb"));
LCS.Add(new CustomSort(0, "pub"));
LCS.Add(new CustomSort(2, "eee"));
LCS.Add(new CustomSort(3, "www"));
foreach (CustomSort cs in LCS) System.Diagnostics.Debug.WriteLine(cs.Name);
LCS.Sort();
foreach (CustomSort cs in LCS) System.Diagnostics.Debug.WriteLine(cs.Name);
}
}
public class CustomSort : Object, INotifyPropertyChanged, IComparable<CustomSort>
{
public event PropertyChangedEventHandler PropertyChanged;
public void OnPropertyChanged(PropertyChangedEventArgs e)
{
if (PropertyChanged != null) PropertyChanged(this, e);
}
private Int16 id;
private string name;
public Int16 ID { get { return id; } }
public String Name { get { return name; } }
public int CompareTo(CustomSort obj)
{
if (this.ID == 0) return -1;
if (obj == null) return 1;
if (obj is CustomSort)
{
CustomSort comp = (CustomSort)obj;
if (comp.ID == 0) return 1;
return string.Compare(this.Name, comp.Name, true);
}
else
{
return 1;
}
}
public override bool Equals(Object obj)
{
// Check for null values and compare run-time types.
if (obj == null) return false;
if (!(obj is CustomSort)) return false;
CustomSort comp = (CustomSort)obj;
return (comp.ID == this.ID);
}
public override int GetHashCode()
{
return (Int32)ID;
}
public CustomSort(Int16 ID, String Name)
{
id = ID;
name = Name;
}
}
}
You need to use a comparison function, they are functions that from two instances of your type return an integer that return 0 if both are equals, a negative value if the first is less than the second and a positive value if the first is greater than the second.
MSDN has a nice table that is easier to follow than text (StackOverflow still doesn't support tables in 2014)
IComparer<T>
Most sort methods accept a custom comparer implementation of type IComparer<T> you should create one encapsulating your custom rules for Group :
class GroupComparer : IComparer<Group>
{
public int Compare(Group a, Group b)
{
if (a != null && b != null && (a.Id == 0 || b.Id == 0))
{
if (a.Id == b.Id)
{
// Mandatory as some sort algorithms require Compare(a, b) and Compare(b, a) to be consistent
return 0;
}
return a.Id == 0 ? -1 : 1;
}
if (a == null || b == null)
{
if (ReferenceEquals(a, b))
{
return 0;
}
return a == null ? -1 : 1;
}
return Comparer<string>.Default.Compare(a.Name, b.Name);
}
}
Usage:
items.OrderBy(_ => _, new GroupAuthorityComparer());
IComparable<T>
If it is the only way to compare Group instances you should make it implement IComparable<T> so that no aditional code is needed if anyone want to sort your class :
class Group : IComparable<Group>
{
...
public int CompareTo(Group b)
{
if (b != null && (Id == 0 || b.Id == 0))
{
if (Id == b.Id)
{
// Mandatory as some sort algorithms require Compare(a, b) and Compare(b, a) to be consistent
return 0;
}
return Id == 0 ? -1 : 1;
}
return Comparer<string>.Default.Compare(Name, b.Name);
}
}
Usage:
items.OrderBy(_ => _.Group);
The choice between one way or the other should be done depending on where this specific comparer is used: Is it the main ordering for this type of item or just the ordering that should be used in one specific case, for example only in some administrative view.
You can even go one level up and provide an IComparable<GroupAuthority> implementation (It's easy once Group implement IComparable<Group>):
class GroupAuthority : IComparable<GroupAuthority>
{
...
public int CompareTo(GroupAuthority b)
{
return Comparer<Group>.Default.Compare(Group, b.Group);
}
}
Usage:
items.OrderBy(_ => _);
The advantage of the last one is that it will be used automatically, so code like: GroupAuthoritys.ToList().Sort() will do the correct thing out of the box.
You can try something like this
list.Sort((x, y) =>
{
if (x.Id == 0)
{
return -1;
}
if (y.Id == 0)
{
return 1;
}
return x.Group.Name.CompareTo(y.Group.Name);
});
Where list is List<T>.
This method takes advantage of custom sort option provided by List<T> using Comparison<T> delegate.
Basically what this method does is, it just adds special condition for comparison when Id, If it is zero it will return a value indicating the object is smaller which makes the object to come in top of the list. If not, it sorts the object using its Group.Name property in ascending order.
public IEnumerable<GroupAuthority> GroupAuthoritysSorted
{
get
{
return GroupAuthoritys.OrderBy(x => x.Group.ID == 0)
.ThenBy(x => x.Group.Name);
}
}

remove duplicates from linq query c#

This is my linq query and I get lots of duplicates with school names.
so I created a regex function to trim the text:
public static string MyTrimmings(string str)
{
return Regex.Replace(str, #"^\s*$\n", string.Empty, RegexOptions.Multiline).TrimEnd();
}
the text gets trimed alright, however, the dropdown values are all duplicates! please help me eliminate duplicates, oh Linq joy!!
ViewBag.schools = new[]{new SelectListItem
{
Value = "",
Text = "All"
}}.Concat(
db.Schools.Where(x => (x.name != null)).OrderBy(o => o.name).ToList().Select(s => new SelectListItem
{
Value = MyTrimmings(s.name),
Text = MyTrimmings(s.name)
}).Distinct()
);
Distinct is poor, GroupBy for the win:
db.Schools.GroupBy(school => school.name).Select(grp => grp.First());
Assuming you have a School class you can write an IEqualityComparer
class SchoolComparer : IEqualityComparer<School>
{
public bool Equals(School x, School y)
{
//Check whether the compared objects reference the same data.
if (Object.ReferenceEquals(x, y)) return true;
//Check whether any of the compared objects is null.
if (Object.ReferenceEquals(x, null) || Object.ReferenceEquals(y, null))
return false;
//Check whether the school' properties are equal.
return x.Name == y.Name;
}
// If Equals() returns true for a pair of objects
// then GetHashCode() must return the same value for these objects.
public int GetHashCode(School school)
{
//Check whether the object is null
if (Object.ReferenceEquals(school, null)) return 0;
//Get hash code for the Name field if it is not null.
int hashSchoolName = school.Name == null ? 0 : school.Name.GetHashCode();
//Calculate the hash code for the school.
return hashSchoolName;
}
}
Then your linq query would look like this:
db.Schools.Where(x => x.name != null)
.OrderBy(o => o.name).ToList()
.Distinct(new SchoolComparer())
.Select(s => new SelectListItem
{
Value = MyTrimmings(s.name),
Text = MyTrimmings(s.name)
});
You could make your class implement the IEquatable<T> interface, so Distinct will know how to compare them. Like this (basic example):
public class SelectListItem : IEquatable<SelectListItem>
{
public string Value { get; set; }
public string Text { get; set; }
public bool Equals(SelectListItem other)
{
if (other == null)
{
return false;
}
return Value == other.Value && Text == other.Text;
}
public override int GetHashCode()
{
unchecked
{
int hash = 17;
if (Value != null)
{
hash = hash * 23 + Value.GetHashCode();
}
if (Text != null)
{
hash = hash * 23 + Text.GetHashCode();
}
return hash;
}
}
}
(GetHashCode taken fron John Skeet's answer here: https://stackoverflow.com/a/263416/249000)

Group a list by another list within it with LINQ

I'm looking to group a list based on a list within that list itself, given the following data structure:
public class AccDocumentItem
{
public string AccountId {get;set;}
public List<AccDocumentItemDetail> DocumentItemDetails {get;set;}
}
And
public class AccDocumentItemDetail
{
public int LevelId {get;set;}
public int DetailAccountId {get;set;}
}
I now have a List<AccDocumentItem> comprised of 15 items, each of those items has a list with variable number of AccDocumentItemDetail's, the problem is that there may be AccDocumentItems that have identical AccDocumentItemDetails, so I need to group my List<AccDocumentItem> by it's AccDocumentItemDetail list.
To make it clearer, suppose the first 3 (of the 15) elements within my List<AccDocumentItem> list are:
1:{
AccountId: "7102",
DocumentItemDetails:[{4,40001},{5,40003}]
}
2:{
AccountId: "7102",
DocumentItemDetails:[{4,40001},{6,83003},{7,23423}]
}
3:{
AccountId: "7102",
DocumentItemDetails:[{4,40001},{5,40003}]
}
How can I group my List<AccDocumentItem> by it's DocumentItemDetails list such that row 1 and 3 are in their own group, and row 2 is in another group?
Thanks.
You could group by the comma separated string of detail-ID's:
var query = documentItemList
.GroupBy(aci => new{
aci.AccountId,
detailIDs = string.Join(",", aci.DocumentItemDetails
.OrderBy(did => did.DetailAccountId)
.Select(did => did.DetailAccountId))
});
Another, more ( elegant,efficient,maintainable ) approach is to create a custom IEqualityComparer<AccDocumentItem>:
public class AccDocumentItemComparer : IEqualityComparer<AccDocumentItem>
{
public bool Equals(AccDocumentItem x, AccDocumentItem y)
{
if (x == null || y == null)
return false;
if (object.ReferenceEquals(x, y))
return true;
if (x.AccountId != y.AccountId)
return false;
return x.DocumentItemDetails
.Select(d => d.DetailAccountId).OrderBy(i => i)
.SequenceEqual(y.DocumentItemDetails
.Select(d => d.DetailAccountId).OrderBy(i => i));
}
public int GetHashCode(AccDocumentItem obj)
{
if (obj == null) return int.MinValue;
int hash = obj.AccountId.GetHashCode();
if (obj.DocumentItemDetails == null)
return hash;
int detailHash = 0;
unchecked
{
foreach (var detID in obj.DocumentItemDetails.Select(d => d.DetailAccountId))
detailHash = detailHash * 23 + detID;
}
return hash + detailHash;
}
}
Now you can use it for GroupBy:
var query = documentItemList.GroupBy(aci => aci, new AccDocumentItemComparer());
You can use that for many other Linq extension methods like Enumerable.Join etc. also.

Compare Properties automatically

I want to get the names of all properties that changed for matching objects. I have these (simplified) classes:
public enum PersonType { Student, Professor, Employee }
class Person {
public string Name { get; set; }
public PersonType Type { get; set; }
}
class Student : Person {
public string MatriculationNumber { get; set; }
}
class Subject {
public string Name { get; set; }
public int WeeklyHours { get; set; }
}
class Professor : Person {
public List<Subject> Subjects { get; set; }
}
Now I want to get the objects where the Property values differ:
List<Person> oldPersonList = ...
List<Person> newPersonList = ...
List<Difference> = GetDifferences(oldPersonList, newPersonList);
public List<Difference> GetDifferences(List<Person> oldP, List<Person> newP) {
//how to check the properties without casting and checking
//for each type and individual property??
//can this be done with Reflection even in Lists??
}
In the end I would like to have a list of Differences like this:
class Difference {
public List<string> ChangedProperties { get; set; }
public Person NewPerson { get; set; }
public Person OldPerson { get; set; }
}
The ChangedProperties should contain the name of the changed properties.
I've spent quite a while trying to write a faster reflection-based solution using typed delegates. But eventually I gave up and switched to Marc Gravell's Fast-Member library to achieve higher performance than with normal reflection.
Code:
internal class PropertyComparer
{
public static IEnumerable<Difference<T>> GetDifferences<T>(PropertyComparer pc,
IEnumerable<T> oldPersons,
IEnumerable<T> newPersons)
where T : Person
{
Dictionary<string, T> newPersonMap = newPersons.ToDictionary(p => p.Name, p => p);
foreach (T op in oldPersons)
{
// match items from the two lists by the 'Name' property
if (newPersonMap.ContainsKey(op.Name))
{
T np = newPersonMap[op.Name];
Difference<T> diff = pc.SearchDifferences(op, np);
if (diff != null)
{
yield return diff;
}
}
}
}
private Difference<T> SearchDifferences<T>(T obj1, T obj2)
{
CacheObject(obj1);
CacheObject(obj2);
return SimpleSearch(obj1, obj2);
}
private Difference<T> SimpleSearch<T>(T obj1, T obj2)
{
Difference<T> diff = new Difference<T>
{
ChangedProperties = new List<string>(),
OldPerson = obj1,
NewPerson = obj2
};
ObjectAccessor obj1Getter = ObjectAccessor.Create(obj1);
ObjectAccessor obj2Getter = ObjectAccessor.Create(obj2);
var propertyList = _propertyCache[obj1.GetType()];
// find the common properties if types differ
if (obj1.GetType() != obj2.GetType())
{
propertyList = propertyList.Intersect(_propertyCache[obj2.GetType()]).ToList();
}
foreach (string propName in propertyList)
{
// fetch the property value via the ObjectAccessor
if (!obj1Getter[propName].Equals(obj2Getter[propName]))
{
diff.ChangedProperties.Add(propName);
}
}
return diff.ChangedProperties.Count > 0 ? diff : null;
}
// cache for the expensive reflections calls
private Dictionary<Type, List<string>> _propertyCache = new Dictionary<Type, List<string>>();
private void CacheObject<T>(T obj)
{
if (!_propertyCache.ContainsKey(obj.GetType()))
{
_propertyCache[obj.GetType()] = new List<string>();
_propertyCache[obj.GetType()].AddRange(obj.GetType().GetProperties().Select(pi => pi.Name));
}
}
}
Usage:
PropertyComparer pc = new PropertyComparer();
var diffs = PropertyComparer.GetDifferences(pc, oldPersonList, newPersonList).ToList();
Performance:
My very biased measurements showed that this approach is about 4-6 times faster than the Json-Conversion and about 9 times faster than ordinary reflections. But in fairness, you could probably speed up the other solutions quite a bit.
Limitations:
At the moment my solution doesn't recurse over nested lists, for example it doesn't compare individual Subject items - it only detects that the subjects lists are different, but not what or where. However, it shouldn't be too hard to add this feature when you need it. The most difficult part would probably be to decide how to represent these differences in the Difference class.
We start with 2 simple methods:
public bool AreEqual(object leftValue, object rightValue)
{
var left = JsonConvert.SerializeObject(leftValue);
var right = JsonConvert.SerializeObject(rightValue);
return left == right;
}
public Difference<T> GetDifference<T>(T newItem, T oldItem)
{
var properties = typeof(T).GetProperties();
var propertyValues = properties
.Select(p => new {
p.Name,
LeftValue = p.GetValue(newItem),
RightValue = p.GetValue(oldItem)
});
var differences = propertyValues
.Where(p => !AreEqual(p.LeftValue, p.RightValue))
.Select(p => p.Name)
.ToList();
return new Difference<T>
{
ChangedProperties = differences,
NewItem = newItem,
OldItem = oldItem
};
}
AreEqual just compares the serialized versions of two objects using Json.Net, this keeps it from treating reference types and value types differently.
GetDifference checks the properties on the passed in objects and compares them individually.
To get a list of differences:
var oldPersonList = new List<Person> {
new Person { Name = "Bill" },
new Person { Name = "Bob" }
};
var newPersonList = new List<Person> {
new Person { Name = "Bill" },
new Person { Name = "Bobby" }
};
var diffList = oldPersonList.Zip(newPersonList, GetDifference)
.Where(d => d.ChangedProperties.Any())
.ToList();
Everyone always tries to get fancy and write these overly generic ways of extracting data. There is a cost to that.
Why not be old school simple.
Have a GetDifferences member function Person.
virtual List<String> GetDifferences(Person otherPerson){
var diffs = new List<string>();
if(this.X != otherPerson.X) diffs.add("X");
....
}
In inherited classes. Override and add their specific properties. AddRange the base function.
KISS - Keep it simple. It would take you 10 minutes of monkey work to write it, and you know it will be efficient and work.
I am doing it by using this:
//This structure represents the comparison of one member of an object to the corresponding member of another object.
public struct MemberComparison
{
public static PropertyInfo NullProperty = null; //used for ROOT properties - i dont know their name only that they are changed
public readonly MemberInfo Member; //Which member this Comparison compares
public readonly object Value1, Value2;//The values of each object's respective member
public MemberComparison(PropertyInfo member, object value1, object value2)
{
Member = member;
Value1 = value1;
Value2 = value2;
}
public override string ToString()
{
return Member.name+ ": " + Value1.ToString() + (Value1.Equals(Value2) ? " == " : " != ") + Value2.ToString();
}
}
//This method can be used to get a list of MemberComparison values that represent the fields and/or properties that differ between the two objects.
public static List<MemberComparison> ReflectiveCompare<T>(T x, T y)
{
List<MemberComparison> list = new List<MemberComparison>();//The list to be returned
if (x.GetType().IsArray)
{
Array xArray = x as Array;
Array yArray = y as Array;
if (xArray.Length != yArray.Length)
list.Add(new MemberComparison(MemberComparison.NullProperty, "array", "array"));
else
{
for (int i = 0; i < xArray.Length; i++)
{
var compare = ReflectiveCompare(xArray.GetValue(i), yArray.GetValue(i));
if (compare.Count > 0)
list.AddRange(compare);
}
}
}
else
{
foreach (PropertyInfo m in x.GetType().GetProperties())
//Only look at fields and properties.
//This could be changed to include methods, but you'd have to get values to pass to the methods you want to compare
if (!m.PropertyType.IsArray && (m.PropertyType == typeof(String) || m.PropertyType == typeof(double) || m.PropertyType == typeof(int) || m.PropertyType == typeof(uint) || m.PropertyType == typeof(float)))
{
var xValue = m.GetValue(x, null);
var yValue = m.GetValue(y, null);
if (!object.Equals(yValue, xValue))//Add a new comparison to the list if the value of the member defined on 'x' isn't equal to the value of the member defined on 'y'.
list.Add(new MemberComparison(m, yValue, xValue));
}
else if (m.PropertyType.IsArray)
{
Array xArray = m.GetValue(x, null) as Array;
Array yArray = m.GetValue(y, null) as Array;
if (xArray.Length != yArray.Length)
list.Add(new MemberComparison(m, "array", "array"));
else
{
for (int i = 0; i < xArray.Length; i++)
{
var compare = ReflectiveCompare(xArray.GetValue(i), yArray.GetValue(i));
if (compare.Count > 0)
list.AddRange(compare);
}
}
}
else if (m.PropertyType.IsClass)
{
var xValue = m.GetValue(x, null);
var yValue = m.GetValue(y, null);
if ((xValue == null || yValue == null) && !(yValue == null && xValue == null))
list.Add(new MemberComparison(m, xValue, yValue));
else if (!(xValue == null || yValue == null))
{
var compare = ReflectiveCompare(m.GetValue(x, null), m.GetValue(y, null));
if (compare.Count > 0)
list.AddRange(compare);
}
}
}
return list;
}
Here you have a code which does what you want with Reflection.
public List<Difference> GetDifferences(List<Person> oldP, List<Person> newP)
{
List<Difference> allDiffs = new List<Difference>();
foreach (Person oldPerson in oldP)
{
foreach (Person newPerson in newP)
{
Difference curDiff = GetDifferencesTwoPersons(oldPerson, newPerson);
allDiffs.Add(curDiff);
}
}
return allDiffs;
}
private Difference GetDifferencesTwoPersons(Person OldPerson, Person NewPerson)
{
MemberInfo[] members = typeof(Person).GetMembers();
Difference returnDiff = new Difference();
returnDiff.NewPerson = NewPerson;
returnDiff.OldPerson = OldPerson;
returnDiff.ChangedProperties = new List<string>();
foreach (MemberInfo member in members)
{
if (member.MemberType == MemberTypes.Property)
{
if (typeof(Person).GetProperty(member.Name).GetValue(NewPerson, null).ToString() != typeof(Person).GetProperty(member.Name).GetValue(OldPerson, null).ToString())
{
returnDiff.ChangedProperties.Add(member.Name);
}
}
}
return returnDiff;
}

Categories