I was reading about the method CollectionAssert.AreEquivalent in a MSDN article and according to MSDN:
Two collections are equivalent if they have the same elements in the same quantity, but in any order. Elements are equal if their values are equal, not if they refer to the same object.
I tried the following code in Visual Studio:
var first = new TradeData { ID = "A", MarketPrice = 0 };
var mockFir = new TradeData { ID = "A", MarketPrice = 0 };
var collection = new List<TradeData> { first };
var mockCollection = new List<TradeData> { mockFir };
CollectionAssert.AreEquivalent(collection, mockCollection);
But I got an Exception:
CollectionAssert.AreEquivalent failed
So, my question is: what exactly MSDN mean when they say that "Elements are equal if their values are equal, not if they refer to the same object"?
Because the TradeData class does not override object.Equals, the base implementation takes over, which compares two objects by reference. Although first and mockFir contain the same values they are not the same object and so they are not considered equal. If you override Equals in the TradeData class your example will work.
"Elements are equal if their values are equal, not if they refer to the same object"
It means that CollectionAssert.AreEquivalent uses Object.equals and not Object.ReferenceEquals. Depending on the Object type and implementation of Object.Equals, values will (rather "can") be used for comparison.
Object.Equals has the following default behaviour for the two Object types (paraphrasing):
Value types are equal if they are of the same type and their public and private fields have the same value.
Reference types are equal when they are the same object. For reference types a call to Equals is equivalent to a call to ReferenceEquals. Reference equality means that the object variables refer to the same object.
The assertion failed in the above code sample because you created different objects to insert in both Lists. AreEquivalent compares objects using Object.Equals method. The default implementation of equals method will return true for REference type objects only if pointing to the same object. To compare the actual value of these objects, you will need to override the equals method for TradeData.
It is a bummer that there is an alternate way to use comparator for CollectionAssert.AreEqual, but not so for CollectionAssert.AreEquivalent.
https://msdn.microsoft.com/en-us/library/ms243753.aspx
Related
When you perform a union in linq to sql, will it be by reference - if the reference to the object point to the same object - or is it by the id field?
Example method:
public IEnumerable<CustomerModel> GetCustomers(string searchCriteria)
{
ExpressLogger.LogDebug("Enter");
try
{
var datacontext = Helpers.Utils.GetCustomerRmhDataContext();
using(datacontext)
{
var accountCustomers = datacontext.Customers.Where(c =>
c.AccountNumber.ToLower().Contains(searchCriteria.ToLower())).Select(c=>convertRmhToModel(c,datacontext)).ToList();
var phoneCustomers = datacontext.Customers.Where(c =>
c.PhoneNumber.ToLower().Contains(searchCriteria.ToLower())).Select(c => convertRmhToModel(c, datacontext)).ToList();
var personalNumberCustomers = datacontext.Customers.Where(c =>
c.TaxNumber.ToLower().Contains(searchCriteria.ToLower())).Select(c => convertRmhToModel(c, datacontext)).ToList();
var emailCustomers = datacontext.Customers.Where(c =>
c.EmailAddress.ToLower().Contains(searchCriteria.ToLower())).Select(c => convertRmhToModel(c, datacontext)).ToList();
var allCustomers = accountCustomers.Union(phoneCustomers).Union(personalNumberCustomers).Union(emailCustomers).ToList();
return allCustomers;
}
}
catch (Exception ex)
{
ExpressLogger.LogError(ex.Message);
throw;
}
}
This function do what i want, it searches for customer for accountnumber, phone, mail and swedish personalnumber. And then i make a union and return the result. But im curios - how does this work, will it compare the union with the ID field or is it by reference - will it check hashcode and equal comparer? (is it the same for intersect and except?)
At this point, .Union is called on .Net collections and its behavior is defined in the docs for Enumerable.Union:
The default equality comparer, Default, is used to compare values of the types that implement the IEqualityComparer generic interface.
This is further clarified in the docs for EqualityComparer<T>.Default:
The Default property checks whether type T implements the System.IEquatable<T> interface and, if so, returns an EqualityComparer<T> that uses that implementation. Otherwise, it returns an EqualityComparer<T> that uses the overrides of Object.Equals and Object.GetHashCode provided by T.
So, if CustomerModel implements IEquatable<CustomerModel>.Equals and/or overrides Object.Equals, then Union uses one of these methods to compare customers.
Otherwise, it falls back on default implementation of Object.Equals that compares object references:
If the current instance is a reference type, the Equals(Object) method tests for reference equality, and a call to the Equals(Object) method is equivalent to a call to the ReferenceEquals method..
I am trying to build the most budget of ORMs.
I have 2 objects, one "item" and one "original", when creating item, i simply save a COPY of it, in a variable called original. The copy is created via an extension i stole from this post https://stackoverflow.com/a/11308879/10647851
So i want to build my update query, very simple i thought.
foreach (PropertyInfo prop in properties) {
var one = prop.GetValue(original, null);
var typeOne = one.GetType();
var two = prop.GetValue(item, null);
var typeTwo = two.GetType();
if(one != two) { ... }
}
However, one is unequal to two in several cases where you would not expect it.
The ID (type in the model = int?) 2703 is unequal to 2703. I stepped through the code and the type is Int32. Booleans are problems too. Strings work. DateTime?'s work.
Use
if(!object.Equals(one, two)) { ... }
for equality comparison instead. That will invoke the default equality comparator or the overridden one for those types that have implemented it (which includes all value types like int on your example).
!= uses reference equality and therefore produces the same value when the referenced types are exactly the same instance. Using != will give you unexpected results due to the fact you are using GetValue which returns an object.
public object GetValue(object obj)
because one and two are actually reference types (i.e. they are boxed ints instead of value types) the "==" comparator performs a reference equality check.
I'm currently trying to compare to data contracts using reflection.
The data contract represents a configuration for a virtual machine. They are both the same class type. I'm trying to log every change in the configuration when setting a new config. However, the data contracts have a large number of members. It'd be rather tedious to do it manually and it does not accommodate new data members being added later.
I tried using reflection to do it. I've managed to get the values of the data members, but depending on the actual type, comparing them will vary.
Some of the members being compared are :
IEnumerable<Subnets>
IEnumerable<IP Address>
X509Certificates
Enums
and a few more
I'm trying to find a way to cast the property to it's actual class, but everything I've found thus far has failed to work.
Here is an example of what I've done so far with reflection.
ExampleClass firstExample = new ExampleClass("a", 1);
ExampleClass secondExample = new ExampleClass("a", 2);
List<string> exampleList = new List<string>()
{
"a",
"b"
};
firstExample.ValueThree = exampleList;
secondExample.ValueThree = exampleList;
firstExample.FourthValue = new List<string>()
{
"c",
"d"
};
secondExample.FourthValue = new List<string>()
{
"c",
"d"
};
Type exampleType = firstExample.GetType();
foreach (PropertyInfo item in exampleType.GetProperties())
{
var firstExampleValue = item.GetValue(firstExample);
var secondExampleValue = item.GetValue(secondExample);
string propName = item.Name;
bool areEqual = firstExampleValue.Equals(secondExampleValue);
Console.WriteLine(propName);
Console.WriteLine(string.Format("\nFirst Value : {0}\nSecond Value : {1}", firstExampleValue, secondExampleValue));
Console.WriteLine(string.Format("Values are equal : {0}\n\n", areEqual));
}
Is there a practical way to convert these to there actual classes as defined by the data contract? I've looked into Convert.ChangeType but found that this will not work for a good portion of the members as they are not IConvertable.
Thanks,
Edwin
This isn't about getting the underlying type. This is about what is considered "equal".
In .NET, every type inherits from object and thus inherits Object.Equals(). But for reference types Object.Equals() will only compare whether the two objects are the exact same object, not whether their contents are equal.
For example, new List<string>() == new List<string>() will always be false.
That's why firstExample == secondExample and firstExampleValue.Equals(secondExampleValue) will always be false.
However, classes can override the Equals method. For example, String overrides Equals() so that it compares the contents of both.
In this case, if ExampleClass is a class that you control, you can override Equals and tell it to compare the contents of each property (and the contents of each List) to decide if they're equal. Once you do that, then you can just write firstExample == secondExample and it would be true.
The documentation of Object.Equals() actually has a simple example of that: https://learn.microsoft.com/en-us/dotnet/api/system.object.equals
Update: Casting to the underlying type doesn't give you any advantage. Even if a type is cast to object, it still uses Equals from the underlying type when you compare. For example, this results in true: ((object)"hello") == ((object)"hello") because it uses String.Equals not Object.Equals.
So your real problem is knowing whether the type has overridden Equals. You can test if a type has overridden Equals like this:
var type = typeof(string);
var overriddenEquals = type.GetMember("Equals").First().DeclaringType != typeof(object);
But that still doesn't help you for types that haven't overridden equals. How do you compare them?
The only way to do this is to be aware of the types that will be used and test for those specifically (a bunch of if statements) and decide how you will compare them. (for lists, you can test for is IEnumerable, loop through it, then test the type inside the list)
And in those cases, it doesn't matter if the type implements IConvertible since you will know what the type is and can cast directly to it, like:
if (obj is Subnets objSubnets) {
//compare objSubnets to another Subnets object
}
I have a condition that compares two integers, but it never returns true even tho both numbers will be equal.
foreach (TreeViewItem item in Categories.Items)
{
if (subCategory.Tag == item.Tag)
{
item.Items.Add(subCategory);
}
}
both are properties of TreeviewItem
TreeViewItem catItem = new TreeViewItem();
catItem.Tag = (int)row["CategoryID"];
Categories.Items.Add(catItem);
And
TreeViewItem subCategory = new TreeViewItem();
subCategory.Tag = (int)row["CategoryID"];
Even tho both values will become equal, the condition will return false and won't reach item.Items.Add(subCategory);
does anyone know what i'm missing?
both are of the same type (int)...
Thanks
Try using Equals instead:
if (object.Equals(subCategory.Tag, item.Tag)){
//...
}
Comparing as you did will compare references, so they are of course won't be equal.
You can also cast each Tag to int and compare the casted results but using Equals is more convenient in this case.
Note that an object also has an Equals method, you can also use this but it's not safe if the object is null.
//subCategory.Tag should not be null
if (subCategory.Tag.Equals(item.Tag)){
//...
}
TrieViewItem.Tag is of object type. When you assign primitives to object reference they get converted to their corresponding classes, in your case Integer.
When you compare reference types using == operator you (unless the operator is overloaded) compare object references. The == operator will return true only if you compare a reference to itself. As your integer values are wrapped in two different objects, == will never return true.
You can read more about object equality on MSDN.
The correct way to compare in your case is:
if (object.Equals(subCategory.Tag, item.Tag))
{
...
}
I got a scenario to create the anonymous list from the anonymous types, and i achieved that using
public static List<T> MakeList<T>(T itemOftype)
{
List<T> newList = new List<T>();
return newList;
}
static void Main(string[] args)
{
//anonymos type
var xx = new
{
offsetname = x.offName,
RO = y.RO1
};
//anonymos list
var customlist = MakeList(xx);
//It throws an error because i have given the wrong order
customlist.Add(new { RO = y.RO2, offsetname = x.offName });
customlist.Add(new { RO = y.RO3, offsetname = x.offName });
//but this works
customlist.Add(new { offsetname = x.offName, RO = y.RO2 });
customlist.Add(new { offsetname = x.offName, RO = y.RO3 });
}
these are the error messages
System.Collections.Generic.List.Add(AnonymousType#1)'
has some invalid arguments
Argument
'1': cannot convert from
'AnonymousType#2' to 'AnonymousType#1'
whats the reason behind that??
Yes, it's important.
Two anonymous type initializers use the same auto-generated type if the property names and types are the same, in the same order.
The order becomes relevant when hashing; it would have been possible for the type to be generated with a consistent order for calculating a hash value, but it seems simpler to just include the property order as part of what makes a type unique.
See section 7.5.10.6 of the C# 3 spec for details. In particular:
Within the same program, two anonymous
object initializers that specify a
sequence of properties of the same
names and compile-time types in the
same order will produce instances of
the same anonymous type.
whats the reason behind that??
Suppose that order did not matter. Suppose you were on the compiler team. Describe for me the exact behaviour of an implementation of "ToString" on such an anonymous type, such that the implementation meets all user expectations.
I personally cannot come up with one, but perhaps you can.
Yes, the order of fields is significant. Same fields, different order will yield different types.
From the language specification:
"Within the same program, two anonymous object initializers that specify a sequence of properties of the same names and compile-time types in the same order will produce instances of the same anonymous type. "