How to Compare two class objecy with Reflection? - c#

i try to compare two class comp1,comp2 i used method below: ComparerCollection(array_X, array_Y); but there are errors below. Arraylist generated from Ilist. how can i do that?
namespace GenericCollecitonComparer
{
class Program
{
static void Main(string[] args)
{
myClass comp1 = new myClass() { ID = 1, Name = "yusuf" };
myClass comp2 = new myClass() { ID = 1, Name = "yusufk" };
Comparer com = new Comparer(comp1, comp2);
Console.ReadKey();
}
}
public class Comparer
{
public Comparer(myClass x, myClass y)
{
PropertyInfo[] propInfo_X = x.GetType().GetProperties();
PropertyInfo[] propInfo_Y = y.GetType().GetProperties();
ArrayList array_X = new ArrayList();
ArrayList array_Y = new ArrayList();
foreach (PropertyInfo pi in propInfo_X)
array_X.Add(pi.GetValue(x, null));
foreach (PropertyInfo pi in propInfo_Y)
array_Y.Add(pi.GetValue(y, null));
// ComparerCollection(array_X, array_Y); --> Error below
}
public bool ComparerCollection<T>(IList<T> xlist, IList<T> ylist)
{
return xlist.SequenceEqual(ylist);
}
}
public class myClass
{
public int ID { get; set; }
public string Name { get; set; }
}
}
/* Error 1 The type arguments for method '
* GenericCollecitonComparer.Comparer.ComparerCollection<T>(System.Collections.Generic.IList<T>, System.Collections.Generic.IList<T>)'
* cannot be inferred from the usage. Try specifying the type arguments explicitly.
*
*/

The error you receive is due to the fact ArrayList is not a generic class. You can use List<object> instead to make it work.
An alternative implementation:
public class Comparer
{
public bool AreEqual { get; private set; }
public Comparer(myClass x, myClass y)
{
var xProperties = x.GetType().GetProperties();
var yProperties = y.GetType().GetProperties();
var xPropertiesValues = xProperties.Select(pi => pi.GetValue(x, null));
var yPropertiesValues = yProperties.Select(pi => pi.GetValue(y, null));
AreEqual = xPropertiesValues.SequenceEqual(yPropertiesValues);
}
}
And a usage example:
[Test]
public void UsageExample()
{
myClass comp1 = new myClass() { ID = 1, Name = "yusuf" };
myClass comp2 = new myClass() { ID = 1, Name = "yusufk" };
myClass comp3 = new myClass() { ID = 1, Name = "yusuf" };
Comparer comparerOf1And2 = new Comparer(comp1, comp2);
Assert.IsFalse(comparerOf1And2.AreEqual);
Comparer comparerOf1And3 = new Comparer(comp1, comp3);
Assert.IsTrue(comparerOf1And3.AreEqual);
}

Related

how to eliminate duplicate items after merge of 2 list<T> [duplicate]

This question already has answers here:
LINQ's Distinct() on a particular property
(23 answers)
Closed 2 years ago.
I want to remove the one set of item in resultant list, if both Name\Type pair is same in both list. I saw Union, but it's doesn't works. it's give all 4 items.
var data1 = new List<Data>
{
new Data{Name = "N1", Type = "T1"},
new Data{Name = "N2", Type = "T2"},
};
var data2 = new List<Data>
{
new Data{Name = "N1", Type = "T1"},
new Data{Name = "N3", Type = "T3"},
};
var X = data1.Union(data2).Distinct().ToList();
The result should only contains 3 items,
new Data{Name = "N1", Type = "T1"},
new Data{Name = "N2", Type = "T2"},
new Data{Name = "N3", Type = "T3"},
Here is how you should implement and use a comparer:
class Program
{
static void Main()
{
List<Dog> dogs = new List<Dog>() {
new Dog
{
Age = 1,
Name = "Fuffy"
},
new Dog
{
Age = 1,
Name = "Fuffy"
},
new Dog
{
Age = 10,
Name = "Pizza"
}
};
var result = dogs.Distinct(new DogEqualityComparer()).ToList();
}
}
public class Dog
{
public string Name { get; set; }
public int Age { get; set; }
}
public class DogEqualityComparer : IEqualityComparer<Dog>
{
public bool Equals([AllowNull] Dog x, [AllowNull] Dog y)
{
return x?.Age == y?.Age && x?.Name == y?.Name;
}
public int GetHashCode([DisallowNull] Dog obj)
{
return (obj.Name?.GetHashCode() ?? 0) + obj.Age.GetHashCode();
}
}

C# Copy all object parameters to child class object

Suppose I have an object of a parent class which I can't change - for example, an instance of ListBox with long list of parameters. Now I create a child class:
class PlaylistBox : ListBox
{
void CopySettingsFrom(ListBox In)
{
//...what now?
}
}
Question - how can I efficiently make a shallow copy from In object to the new object of PlaylistBox?
Here an example with 3 methods, based on reflection and AutoMapper with explanation:
internal class Program
{
private static void Main(string[] args)
{
Example1();
Example2();
Example3();
}
public static void Example1()
{
Console.WriteLine("This example shows using copy with reflection. Minus of this method - u have to implement FULL copy for each class or u will copy only references to object properties");
//creating new parent class with some values
var parentClass = new ParentClass
{
Property1 = "qqq",
Property2 = 1,
ObjectProperty = new SomeClassWithObjectProperty
{
ObjectProperty = new SomeObjectClass {SomeProperty = "www"}
}
};
//crating new child class and copy REFERENCES to properties
var childClassReflection = new ChildClassReflection(parentClass);
//changing properties of parent
parentClass.Property1 = "rrr";
parentClass.Property2 = 2;
parentClass.ObjectProperty.ObjectProperty.SomeProperty = "eee";
//we will get OLD values for VALUE types and OLD values for REFERENCE types
//qqq 1 WWW
Console.WriteLine(childClassReflection.Property1 + " " + childClassReflection.Property2 + " " + childClassReflection.ObjectProperty.ObjectProperty.SomeProperty);
}
public static void Example2()
{
Console.WriteLine();
Console.WriteLine("This example shows using copy with reflection WITH FULL COPY");
//creating new parent class with some values
var parentClass = new ParentClass
{
Property1 = "qqq",
Property2 = 1,
ObjectProperty = new SomeClassWithObjectProperty
{
ObjectProperty = new SomeObjectClass {SomeProperty = "www"}
}
};
//crating new child class and copy REFERENCES to properties
var childClassReflection = new ChildClassReflectionWithFullCopy(parentClass);
//changing properties of parent
parentClass.Property1 = "rrr";
parentClass.Property2 = 2;
parentClass.ObjectProperty.ObjectProperty.SomeProperty = "eee";
//we will get OLD values for VALUE types and NEW values for REFERENCE types
//qqq 1 eee
Console.WriteLine(childClassReflection.Property1 + " " + childClassReflection.Property2 + " " + childClassReflection.ObjectProperty.ObjectProperty.SomeProperty);
}
public static void Example3()
{
//here i will show copy using AutoMapper
Console.WriteLine();
Console.WriteLine("This example shows using copy with AutoMapper");
//creating new parent class with some values
var parentClass = new ParentClass
{
Property1 = "qqq",
Property2 = 1,
ObjectProperty = new SomeClassWithObjectProperty
{
ObjectProperty = new SomeObjectClass { SomeProperty = "www" }
}
};
Mapper.Initialize(cfg => cfg.CreateMap<ParentClass, ChildClassAutoMapper>());
//crating new child class and copy REFERENCES to properties
var childClassReflection = Mapper.Map<ChildClassAutoMapper>(parentClass);
//changing properties of parent
parentClass.Property1 = "rrr";
parentClass.Property2 = 2;
parentClass.ObjectProperty.ObjectProperty.SomeProperty = "eee";
//we will get OLD values for VALUE types and OLD values for REFERENCE types
//qqq 1 eee
Console.WriteLine(childClassReflection.Property1 + " " + childClassReflection.Property2 + " " + childClassReflection.ObjectProperty.ObjectProperty.SomeProperty);
}
}
public class ChildClassAutoMapper:ParentClass
{
}
public class ChildClassReflection : ParentClass
{
public ChildClassReflection(ParentClass parentClass)
{
foreach (var p in ParentProperties)
p.SetMethod.Invoke(this, new[] {p.GetMethod.Invoke(parentClass, null)});
}
//do it only once for best performance
private static PropertyInfo[] ParentProperties { get; } = typeof(ParentClass).GetProperties().Where(c => c.CanRead && c.CanWrite).ToArray();
}
public class ChildClassReflectionWithFullCopy : ParentClass
{
public ChildClassReflectionWithFullCopy(ParentClass parentClass)
{
var parentClassLocal = JsonConvert.DeserializeObject<ParentClass>(JsonConvert.SerializeObject(parentClass));
foreach (var p in ParentProperties)
p.SetMethod.Invoke(this, new[] {p.GetMethod.Invoke(parentClassLocal, null)});
}
//do it only once for best performance
private static PropertyInfo[] ParentProperties { get; } = typeof(ParentClass).GetProperties().Where(c => c.CanRead && c.CanWrite).ToArray();
}
public class ParentClass
{
public string Property1 { get; set; }
public int Property2 { get; set; }
public SomeClassWithObjectProperty ObjectProperty { get; set; }
}
public class SomeClassWithObjectProperty
{
public SomeObjectClass ObjectProperty { get; set; }
}
public class SomeObjectClass
{
public string SomeProperty { get; set; }
}
You can use reflection
//Other Imports...
using System.Reflection;
public PlaylistBox(ListBox In)
{
PropertyInfo[] properties = typeof(ListBox).GetProperties();
foreach (PropertyInfo p in properties)
if (p.CanRead && p.CanWrite)
p.SetMethod.Invoke(this, new object[] { p.GetMethod.Invoke(In, null) });
}
For .NET < 4.5, substitute calls to the GetMethod and SetMethod properties with calls to the GetGetMethod() and GetSetMethod() methods respectively.

Generic List that holds a generic list

I was wondering if there was a method in which I can hold a generic list that can hold a generic list.
My issue is that I have a list that can hold either LIST A or LIST B or LIST C.
I figured out how to do this with Data types but I want this list to be able to hold classes that I create.
For example:
List<T> listA = new List<T>();
Where T is ObjectX
listA.Add(new list<Y> { new List<U>() { new List<T>() } } );
Where Y is ObjectY<br>
Where U is ObjectU
etc.
EDIT:
Let me put it into context.
I have a list of Objects called Suites
Each Suite can have a list of CaseObjects OR a list of CaseHolderObjects.
Each CaseHolder can hold a list of CaseObjects
Each Case can hold a list of ActionObjects
I think this is what you want:
using System;
using System.Collections.Generic;
using System.Linq;
using System.Text.RegularExpressions;
namespace TestList
{
public class Program
{
public static void Main(string[] args)
{
//Use this syntax for a list of list of classes
List<List<Test>> test = new List<List<Test>>();
}
}
//This class is just for example.
class Test
{
//Your class code here
}
}
This is what you wanted, a list that holds any other type of list ^^
class Program
{
static void Main(string[] args)
{
Person p1 = new Person(1, "Mario");
Person p2 = new Person(2, "Franco");
Dog d1 = new Dog(1, "Fuffy");
Dog d2 = new Dog(2, "Chop");
List<Person> listP = new List<Person>();
listP.Add(p1);
listP.Add(p2);
List<Dog> listD = new List<Dog>();
listD.Add(d1);
listD.Add(d2);
List<Object> listO = new List<Object>();
listO.Add(listP);
listO.Add(listD);
}
public class Person
{
public Person(int id, string name)
{
this.id = id;
this.name = name;
}
public int id { get; set; }
public string name { get; set; }
}
public class Dog
{
public Dog(int id, string name)
{
this.id = id;
this.name = name;
}
public int id { get; set; }
public string name { get; set; }
}
}
I solved the issue. The classes in question I had them implement an empty interface. I then created a single property.
List<List<Interfaces.IMetaData>> myList = new List<List<Interfaces.IMetaData>>();
myList.Add(new List<Interfaces.IMetaData>() { new SuiteObject() { } });
myList.Add(new List<Interfaces.IMetaData>() { new CaseHolderObject() { } });
myList.Add(new List<Interfaces.IMetaData>() { new CaseObject() { } });
myList.Add(new List<Interfaces.IMetaData>() { new ActionObject() { } });
You don't need generic collection if you want to store different class in it. Try to use ArrayList (System.Collections namespace). You can add to it any object (of cource cost of performace);
For example:
ArrayList listA = new ArrayList() { 1, true, "string" };
ArrayList ListB = new ArrayList() { 2, false };
ArrayList ListC = new ArrayList() { 3, "string3" };
ListB.Add(ListC);
listA.Add(ListB);

Compare list/IEnumerable type properties in Generic Method

I am trying to compare the object which contains List<> type properties. I am able to compare the simple properties but got stuck with complex one.
foreach (PropertyInfo pi in properties)
{
object oldValue = pi.GetValue(oldObject), newValue = pi.GetValue(newObject);
if (pi.PropertyType.IsGenericType && typeof(IEnumerable).IsAssignableFrom(pi.PropertyType))
{
Type type = oldValue.GetType().GetGenericArguments()[0];
/* Need something like below commented line.*/
// var added = newValue.Except(oldValue)
// var removed = oldValue.Except(newValue);
}}
In if block, i need to find the added and removed object in List type properties. In object we have Key attributed property to find added and removed object.
Well, here is the complete solution according to my understanding of the question.
here is the key attribute that designates key property of an item:
[AttributeUsage(AttributeTargets.Property)]
public class KeyAttribute : Attribute
{
}
For test, let's say we have a class named SomeClass, which contains a List<> property, and an item class named SomeItem, which contains one key property, and additional properties ignored by comparison:
public class SomeClass
{
public List<SomeItem> Items { get; set; }
}
public class SomeItem
{
[Key]
public int TheKey { get; set; }
public string SomeValue { get; set; }
}
Here is the function that performs the comparison:
public void CompareNewWithOld(object oldObject, object newObject, List<object> added, List<object> removed)
{
var properties = typeof (SomeClass).GetProperties();
foreach (PropertyInfo pi in properties)
{
object oldValue = pi.GetValue(oldObject), newValue = pi.GetValue(newObject);
if (pi.PropertyType.IsGenericType && typeof(IEnumerable).IsAssignableFrom(pi.PropertyType))
{
var itemType = pi.PropertyType.GetGenericArguments()[0];
var itemKeyProperty = itemType
.GetProperties()
.FirstOrDefault(ipi => ipi.GetCustomAttribute<KeyAttribute>() != null);
if (itemKeyProperty == null)
{
continue; // no Key property -- cannot compare
}
var comparer = new ItemByKeyEqualityComparer(itemKeyProperty);
HashSet<object> oldSet = new HashSet<object>(((IEnumerable)oldValue).Cast<object>(), comparer);
HashSet<object> newSet = new HashSet<object>(((IEnumerable)newValue).Cast<object>(), comparer);
HashSet<object> removedSet = new HashSet<object>(oldSet, comparer);
removedSet.ExceptWith(newSet);
HashSet<object> addedSet = new HashSet<object>(newSet, comparer);
addedSet.ExceptWith(oldSet);
added.AddRange(addedSet);
removed.AddRange(removedSet);
}
}
}
In order to conveniently compare item objects by their key property with HashSet<T>, we also need to implement an equality comparer class, as follows:
public class ItemByKeyEqualityComparer : IEqualityComparer<object>
{
private readonly PropertyInfo _keyProperty;
public ItemByKeyEqualityComparer(PropertyInfo keyProperty)
{
_keyProperty = keyProperty;
}
public bool Equals(object x, object y)
{
var kx = _keyProperty.GetValue(x);
var ky = _keyProperty.GetValue(y);
if (kx == null)
{
return (ky == null);
}
return kx.Equals(ky);
}
public int GetHashCode(object obj)
{
var key = _keyProperty.GetValue(obj);
return (key == null ? 0 : key.GetHashCode());
}
}
and here is a test that passes:
[Test]
public void TestCompareNewWithOld()
{
var oldObject = new SomeClass() {
Items = new List<SomeItem>() {
new SomeItem() { TheKey = 1, SomeValue = "A"},
new SomeItem() { TheKey = 2, SomeValue = "B"},
new SomeItem() { TheKey = 3, SomeValue = "C"},
new SomeItem() { TheKey = 4, SomeValue = "D"},
}
};
var newObject = new SomeClass() {
Items = new List<SomeItem>() {
new SomeItem() { TheKey = 3, SomeValue = "W"},
new SomeItem() { TheKey = 4, SomeValue = "V"},
new SomeItem() { TheKey = 5, SomeValue = "U"},
new SomeItem() { TheKey = 6, SomeValue = "T"},
}
};
var added = new List<object>();
var removed = new List<object>();
CompareNewWithOld(oldObject, newObject, added, removed);
Assert.That(removed, Is.EquivalentTo(new[] {
oldObject.Items[0], //A
oldObject.Items[1] //B
}));
Assert.That(added, Is.EquivalentTo(new[] {
newObject.Items[2], //U
newObject.Items[3] //T
}));
}
Cast oldValue and newValue to IEnumerable<Object> and then compare them as needed:
if (IsGenericEnumerable(pi)) {
IEnumerable<Object> newEnumerable = (IEnumerable<Object>) newValue;
IEnumerable<Object> oldEnumerable = (IEnumerable<Object>) oldValue;
// operate with newEnumerable and oldEnumerable as needed by the logic
// ...
}

Map list to existing list in Automapper using a key

Automapper easily handles mapping one list of object types to another list of different objects types, but is it possible to have it map to an existing list using an ID as a key?
I have not found better way than the following.
Here are source and destination.
public class Source
{
public int Id { get; set; }
public string Foo { get; set; }
}
public class Destination
{
public int Id { get; set; }
public string Foo { get; set; }
}
Define converter (You should change List<> to whatever type you are using).
public class CollectionConverter: ITypeConverter<List<Source>, List<Destination>>
{
public List<Destination> Convert(ResolutionContext context)
{
var destinationCollection = (List<Destination>)context.DestinationValue;
if(destinationCollection == null)
destinationCollection = new List<Destination>();
var sourceCollection = (List<Source>)context.SourceValue;
foreach(var source in sourceCollection)
{
Destination matchedDestination = null;
foreach(var destination in destinationCollection)
{
if(destination.Id == source.Id)
{
Mapper.Map(source, destination);
matchedDestination = destination;
break;
}
}
if(matchedDestination == null)
destinationCollection.Add(Mapper.Map<Destination>(source));
}
return destinationCollection;
}
}
And here is actual mapping configuration and example.
Mapper.CreateMap<Source,Destination>();
Mapper.CreateMap<List<Source>,List<Destination>>().ConvertUsing(new CollectionConverter());
var sourceCollection = new List<Source>
{
new Source{ Id = 1, Foo = "Match"},
new Source{ Id = 2, Foo = "DoesNotMatchWithDestination"}
};
var destinationCollection = new List<Destination>
{
new Destination{ Id = 1, Foo = "Match"},
new Destination{ Id = 3, Foo = "DoeNotMatchWithSource"}
};
var mergedCollection = Mapper.Map(sourceCollection, destinationCollection);
You should get the following result.
I found this article very useful and as such I thought I would feed back in my generic version of the type converter which you can use to select the property to match on from each object.
Using it all you need to do is:
// Example of usage
Mapper.CreateMap<UserModel, User>();
var converter = CollectionConverterWithIdentityMatching<UserModel, User>.Instance(model => model.Id, user => user.Id);
Mapper.CreateMap<List<UserModel>, List<User>>().ConvertUsing(converter);
//The actual converter
public class CollectionConverterWithIdentityMatching<TSource, TDestination> :
ITypeConverter<List<TSource>, List<TDestination>> where TDestination : class
{
private readonly Func<TSource, object> sourcePrimaryKeyExpression;
private readonly Func<TDestination, object> destinationPrimaryKeyExpression;
private CollectionConverterWithIdentityMatching(Expression<Func<TSource, object>> sourcePrimaryKey, Expression<Func<TDestination, object>> destinationPrimaryKey)
{
this.sourcePrimaryKeyExpression = sourcePrimaryKey.Compile();
this.destinationPrimaryKeyExpression = destinationPrimaryKey.Compile();
}
public static CollectionConverterWithIdentityMatching<TSource, TDestination>
Instance(Expression<Func<TSource, object>> sourcePrimaryKey, Expression<Func<TDestination, object>> destinationPrimaryKey)
{
return new CollectionConverterWithIdentityMatching<TSource, TDestination>(
sourcePrimaryKey, destinationPrimaryKey);
}
public List<TDestination> Convert(ResolutionContext context)
{
var destinationCollection = (List<TDestination>)context.DestinationValue ?? new List<TDestination>();
var sourceCollection = (List<TSource>)context.SourceValue;
foreach (var source in sourceCollection)
{
TDestination matchedDestination = default(TDestination);
foreach (var destination in destinationCollection)
{
var sourcePrimaryKey = GetPrimaryKey(source, this.sourcePrimaryKeyExpression);
var destinationPrimaryKey = GetPrimaryKey(destination, this.destinationPrimaryKeyExpression);
if (string.Equals(sourcePrimaryKey, destinationPrimaryKey, StringComparison.OrdinalIgnoreCase))
{
Mapper.Map(source, destination);
matchedDestination = destination;
break;
}
}
if (matchedDestination == null)
{
destinationCollection.Add(Mapper.Map<TDestination>(source));
}
}
return destinationCollection;
}
private string GetPrimaryKey<TObject>(object entity, Func<TObject, object> expression)
{
var tempId = expression.Invoke((TObject)entity);
var id = System.Convert.ToString(tempId);
return id;
}
}

Categories

Resources