What is the best way to compare two arbitrary dynamic objects for equality? For example these two objects.
I.e.
dynamic obj1 = new ExpandoObject();
obj1.Name = "Marcus";
obj1.Age = 39;
obj1.LengthInMeters = 1.96;
dynamic obj2 = AMethodReturningADynamic();
obj2.Name = "Marcus";
obj2.Age = 39;
obj2.LengthInMeters = 1.96;
Assert.AreEqual(obj1, obj2); // ?
Or is there a way to get the actual properties and their values as lists? To create an ExpandoObject from a dynamic type for example?
The Microsoft API's for dynamically invoking methods and propertys on arbitrary dynamic objects (IDynamicMetaObjectProvider) are not easy to use when you don't have the compiler's help. You can use Dynamitey (via nuget) to simplify this completely. It has a static function Dynamic.InvokeGet to call property's getters with just a target and a property name.
To get a list of properties of the dynamic object, there is a bit of a gotcha, as the dynamic object has to support it (if it's a DynamicObject that means implementing GetDynamicMemberNames, Expando supports it, but random IDynamicMetaObjectProvider may not and just return an empty list). Dynamitey has a method to simplifying getting those names as well, Dynamic.GetMemberNames.
Both of those two functions give you the basic tools necessary to compare many arbitrary dynamic objects via properties.
//using System.Dynamic;
//using Dynamitey;
//using System.Linq;
IEnumerable<string> list1 =Dynamic.GetMemberNames(obj1);
list1 = list1.OrderBy(m=>m);
IEnumerable<string> list2 =Dynamic.GetMemberNames(obj2);
list2 = list2.OrderBy(m=>m);
if(!list1.SequenceEqual(list2))
return false;
foreach(var memberName in list1){
if(!Dynamic.InvokeGet(obj1, memberName).Equals(Dynamic.InvokeGet(obj2,memberName))){
return false;
}
}
return true;
However, if they are just your own DynamicObject subclass then it'd be easier to just follow the typical rules for implementing Equals, there really is no difference from non-dynamic objects, and just compare what you are internally using for state.
ExpandoObject implements ICollection<KeyValuePair<string, object>> (in addition to IDictionary and IEnumerable of the same), so you should be able to compare them property by property pretty easily:
public static bool AreExpandosEquals(ExpandoObject obj1, ExpandoObject obj2)
{
var obj1AsColl = (ICollection<KeyValuePair<string,object>>)obj1;
var obj2AsDict = (IDictionary<string,object>)obj2;
// Make sure they have the same number of properties
if (obj1AsColl.Count != obj2AsDict.Count)
return false;
foreach (var pair in obj1AsColl)
{
// Try to get the same-named property from obj2
object o;
if (!obj2AsDict.TryGetValue(pair.Key, out o))
return false;
// Property names match, what about the values they store?
if (!object.Equals(o, pair.Value))
return false;
}
// Everything matches
return true;
}
See "Enumerating and deleting members" to get the members of an ExpandoObject http://msdn.microsoft.com/en-us/library/system.dynamic.expandoobject.aspx
Arbitrary dynamic objects do not appear to expose enumerators, though.
You have to implement IComparable-Interface. Then you have the appropriate functions needed from .NET/C# to compare two objects with each other.
Expando Objects are useable as an IDictonary<string, object> so you should be able to use that.
Something like
Assert.AreEqual((IDictonary(object, string))obj1, (IDictonary(object, string))obj2);
Edit the AreEqual won't work.
But you could try comparing the two dictionaries fairly simply.
You can also use the ObjectsComparer library available on GitHub :
ObjectsComparer
This library is an object-to-object comparer that allows us to compare objects recursively member by member and to define custom comparison rules for certain properties, fields or types. It supports enumerables (arrays, collections, lists), multidimensional arrays, enumerations, flags and dynamic objects (ExpandoObject, DynamicObject and compiler generated dynamic objects).
Go to Valerii Tereshchenko excellent paper for more details.
Related
LINQ Where() works with System.Collections.Generic.IEnumerable<T> but cant be used if the implemented interface is System.Collections.IEnumerable
My question is, why is that so?
Update:
Maybe a little bit of context, I want to use .Where() on the Transform class of Unity, which implements System.Collections.IEnumerable instead of System.Collections.Generic.IEnumerable<Transform> even though it only has Transforms as children..
So I now created an extension method for Transform, feel free to use it:
/// <summary> Where-Filter implementation for transform to filter out specific children</summary>
public static IEnumerable<GameObject> WhereChild(this Transform s, Func<GameObject, bool> callback) {
List<GameObject> r = new List<GameObject>();
foreach (Transform cur in s) { if (callback(cur.gameObject)) { r.Add(cur.gameObject); } }
return r;
}
(Modify it if you want to work on the transforms and not the children Gameobjects instead, I like it more this way;)
Because most of those methods are generic and make little sense when you only get objects. A few methods are in fact declared as extension methods on IEnumerable instead of IEnumerable<T>, e.g. Cast<T>() and OfType<T>, both of which return a typed enumerable.
So in your case you can use Cast<object>() to reap the benefits of LINQ in the most useless manner, because the predicate for Where cannot really reasonably do much with an object without casting it anyway.
Non generic collections where you can't really say anything about items more that they are just objects won't let you write useful predicates. And these are required by linq operators.
Suppose
IEnumerable e = ...;
e.Where( item => ?? );
Here item is of type object and you are pretty much stuck.
On the other hand
IEnumerable<Person> e = ...;
e.Where( item => ?? );
Here you can refer to whatever members the actual type contains.
Note that you can always "upcast" collections
IEnumerable e = ...;
e.OfType<Person>().Where( ... )
The OfType operator makes a generic collection of these items of a non generic collection that are of given type. Thus it allows you to introduce strong typing and use typed operators further the line.
I have a List<CustomObject> and want to remove duplicates from it.
If two Custom Objects have same value for property: City, then I will call them duplicate.
I have implemented IEquatable as follows, but not able to remove duplicates from the list.
What is missing?
public class CustomAddress : IAddress, IEqualityComparer<IAddress>
{
//Other class members go here
//IEqualityComparer members
public bool Equals(IAddress x, IAddress y)
{
// Check whether the compared objects reference the same data.
if (ReferenceEquals(x, y)) return true;
// Check whether any of the compared objects is null.
if (ReferenceEquals(x, null) || ReferenceEquals(y, null))
return false;
// Check whether the Objects' properties are equal.
return x.City.Equals(y.City);
}
public int GetHashCode(IAddress obj)
{
// Check whether the object is null.
if (ReferenceEquals(obj, null)) return 0;
int hashAreaName = City == null ? 0 : City.GetHashCode();
return hashAreaName;
}
}
I am using .NET 3.5
With your overrides of Equals and GetHashCode in place, if you have an existing list that you need to filter, simply invoke Distinct() (available through the namespace System.Linq) on the list.
var noDupes = list.Distinct();
This will give you a duplicate-free sequence. If you need that to be a concrete list, simply add a ToList() to the end of the invocation.
var noDupes = list.Distinct().ToList();
Another answer mentions implementing an IEqualityComparer<CustomObject>. This is useful when overriding Equals and GetHashCode directly is either impossible (you don't control the source) or does not make sense (your idea of equality in this particular case is not universal for the class). In that case, define the comparer as demonstrated and provide an instance of the comparer to an overload of Distinct.
Finally, if you're building a list from the ground-up and want to avoid duplicates being inserted, you can use a HashSet<T> as mentioned here. The HashSet also accepts a custom comparer in the constructor, so you can optionally include that.
var mySet = new HashSet<CustomObject>();
bool isAdded = mySet.Add(myElement);
// isAdded will be false if myElement already exists in set, and
// myElement would not be added a second time.
// or you could use
if (!mySet.Contains(myElement))
mySet.Add(myElement);
One more option that is not using .NET library methods but can be useful in a pinch is Jon Skeet's DistinctBy, which you can see a rough implementation here. The idea is that you submit a Func<MyObject, Key> lambda expression directly and omit the overrides of Equals and GetHashCode (or the custom comparer) entirely.
var noDupes = list.DistinctBy(obj => obj.City); // NOT part of BCL
Just by implementing .Equals the way you did (wich you implemented correctly) you will not prevent duplicates from beeing added to a List<T>. You will actually have to manually remove them.
Instead of List<CustomObject> use HashSet<CustomObject>. It will never contain duplicates.
That's because List<CustomObject> tests if your class ( CustomObject) implements IEquatable<CustomObject> and not IEquatable<IAddress> as you did
I assume that for duplicate check you are using the Contains method, before adding a new member
To match duplicates on only a specific property you need a comparer.
class MyComparer : IEqualityComparer<CustomObject>
{
public bool Equals(CustomObject x, CustomObject y)
{
return x.City.Equals(y.City);
}
public int GetHashCode(CustomObject x)
{
return x.City.GetHashCode()
}
}
Usage:
var yourDistictObjects = youObjects.Distinct(new MyComparer());
Edit: Found this thread that does what you need and I think I referred to it in the past:
Remove duplicates in the list using linq
One answer that I thought was kind of interesting (but not how had done it) was:
var distinctItems = items.GroupBy(x => x.Id).Select(y => y.First());
It's a one liner that does what you need but might not be as efficient as the other methods.
I need something like Dictionary where dynamic can be anything from string to objects.
But when i use objects, i need to know the type of the object and then access the appropriate properties of those objects.
Is there a way WITHOUT using Reflection.
* EDITED **
I tried to use this :
CloneObject<T, TU>(IDictionary<T, TU> sourceObject)
But if i use this, how can i access T's public fields without using reflection
You can use Hashtable for this purpose
Here is the Examples
http://www.dotnetperls.com/hashtable
You can also use Dictionary which is more efficient than Hashtable
See Examples Here:
http://www.dotnetperls.com/dictionary-keys
I'm confused a little bit. You trying to store any types of objects in your dictionary but access to them without reflection.
If so you can use dynamic types:
Dictionary dict = new Dictionary();
dict["string"] = "some string";
dict["int"] = 25;
dict["my_class"] = new MyClass {SomeProperty = 12};
And then you can access all this values without any casts:
string s1 = dict["string"].Substring(0, 4); // s1 equals to "some"
int propertyValue = dict["my_class"].SomeProperty; // propertyValue equals to 12
where MyClass is:
class MyClass
{
public int SomeProperty {get;set;}
}
Without using reflection, this task cannot be completed. All I have done is create clones of objects separately and then used them.
I have come across something pretty complex. I would be obliged if anyone can help.
1) I have to create a List<> of unknown type at compile time. That I have already achieved.
Type customList = typeof(List<>).MakeGenericType(tempType);
object objectList = (List<object>)Activator.CreateInstance(customList);
"temptype" is the custom type thats been already fetched.
2) Now I have PropertyInfo object which is that list from which I have to copy all items to the the instance that I have just created "objectList"
3) Then I need to iterate and access the items of "objectList" as if it were a "System.Generic.List".
Cutting long story short, using reflection I need to extract a property that is a list and have it as an instance for further use. Your suggestions would be appreciated. Thanks in Advance.
Umair
Many of the .NET generic collection classes also implement their non-generic interfaces. I'd make use of these to write your code.
// Create a List<> of unknown type at compile time.
Type customList = typeof(List<>).MakeGenericType(tempType);
IList objectList = (IList)Activator.CreateInstance(customList);
// Copy items from a PropertyInfo list to the object just created
object o = objectThatContainsListToCopyFrom;
PropertyInfo p = o.GetType().GetProperty("PropertyName");
IEnumerable copyFrom = p.GetValue(o, null);
foreach(object item in copyFrom) objectList.Add(item); // Will throw exceptions if the types don't match.
// Iterate and access the items of "objectList"
// (objectList declared above as non-generic IEnumerable)
foreach(object item in objectList) { Debug.WriteLine(item.ToString()); }
Do you think this would help you? Efficient way of updating a collection from another collection
I came up with something similar. I borrowed the SetProperties() method from NullSkull and wrote a simple method that calls the NullSkull SetProperties():
public static List<U> CopyList<T, U>(List<T> fromList, List<U> toList)
{
PropertyInfo[] fromFields = typeof(T).GetProperties();
PropertyInfo[] toFields = typeof(U).GetProperties();
fromList.ForEach(fromobj =>
{
var obj = Activator.CreateInstance(typeof(U));
Util.SetProperties(fromFields, toFields, fromobj, obj);
toList.Add((U)obj);
});
return toList;
}
...so with one line of code I can retrieve a List<desired class> populated with matching values by name from List<source class> as follows:
List<desired class> des = CopyList(source_list, new List<desired class>());
As far as performance goes, I didn't test it, as my requirements call for small lists.
I have two lists A and B, at the beginning of my program, they are both filled with information from a database (List A = List B). My program runs, List A is used and modified, List B is left alone. After a while I reload List B with new information from the database, and then do a check with that against List A.
foreach (CPlayer player in ListA)
if (ListB.Contains(player))
-----
Firstly, the object player is created from a class, its main identifier is player.Name.
If the Name is the same, but the other variables are different, would the .Contains still return true?
Class CPlayer(
public CPlayer (string name)
_Name = name
At the ---- I need to use the item from ListB that causes the .Contains to return true, how do I do that?
The default behaviour of List.Contains is that it uses the default equality comparer. If your items are reference types this means that it will use an identity comparison unless your class provides another implementation via Equals.
If you are using .NET 3.5 then you can change your second line to this which will do what you want:
if (ListB.Any(x => x.Name == player.Name))
For .NET 2.0 you could implement Equals and GetHashCode for your class, but this might give undesirable behaviour in other situations where you don't want two player objects to compare equal if they have the same name but differ in other fields.
An alternative way is to adapt Jon Skeet's answer for .NET 2.0. Create a Dictionary<string, object> and fill it with the names of all players in listB. Then to test if a player with a certain name is in listB you can use dict.ContainsKey(name).
An alternative to Mark's suggestion is to build a set of names and use that:
HashSet<string> namesB = new HashSet<string>(ListB.Select(x => x.Name));
foreach (CPlayer player in ListA)
{
if (namesB.Contains(player.Name))
{
...
}
}
Assuming you are using the System.Collections.Generic.List class, if the CPlayer class does not implement IEquatable<T> it will use the Equals and GetHashCode functions of the CPlayer class to check if the List has a member that equals the argument of Contains. Assuming that implementation is OK for you, you could something like
CPlayer listBItem = ListB.First(p => p == player);
to get the instance from ListB
It sounds like this is what you need to accomplish:
For each player in list A, find each player in list B with the same name and bring both players into the same scope.
Here is an approach which joins the two lists in a query:
var playerPairs =
from playerA in ListA
join playerB in ListB on playerA.Name equals playerB.Name
select new { playerA, playerB };
foreach(var playerPair in playerPairs)
{
Console.Write(playerPair.playerA.Name);
Console.Write(" -> ");
Console.WriteLine(playerPair.playerB.Name);
}
If you want the .Contains method to match only on CPlayer.Name, then in the CPlayer class implement these methods:
public override bool Equals(object obj)
{
if (!(obj is CPlayer)
return false;
return Name == (obj as CPlayer).Name;
}
public override int GetHashCode()
{
return Name.GetHashCode();
}
If you want the Name comparison to be Case Insensitive, replace use this Equals method instead:
public override bool Equals(object obj)
{
if (!(obj is CPlayer)
return false;
return Name.Equals((obj as CPlayer).Name, StringComparison.OrdinalIgnoreCase);
}
If you do this, your .Contains call will work just as you want it.
Secondly, if you want to select this item in the list, do this:
var playerB = ListB[ListB.IndexOf(player)];
It uses the same .Equals and .GetHashCode methods.
UPD:
This is probably a subjective statement, but you could also squeeze some performance out of it, if your .Equals method compared the Int hashes before doing the string comparison..
Looking at the .NET sources (Reflector FTW) I can see that seemingly only the HastTable class uses GetHashCode to improve it's performance, instead of using .Equals to compare objects every single time. In the case of a small class like this, the equality comparer is simple, a single string comparison.. If you were comparing all properties though, then comparing two integers would be much faster (esp if they were cached :) )
The List.Contains and List.IndexOf don't use the hash code, and use the .Equals method, hence I proposed checking the hash code inside. It probably won't be anything noticeable, but when you're itching to get every single ms of execution (not always a good thing, bug hey! :P ) this might help someone. just saying... :)