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))
{
...
}
Related
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 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
I have an extension method to safe casting objects, that looks like this:
public static T SafeCastAs<T>(this object obj) {
if (obj == null)
return default(T);
// which one I should use?
// 1. IsAssignableFrom
if (typeof(T).IsAssignableFrom(obj.GetType()))
return (T)obj;
// 2. IsInstanceOfType
if (typeof(T).IsInstanceOfType(obj))
return (T) obj;
// 3. is operator
if (obj is T)
return (T) obj;
return default(T);
}
As you can see, I have 3 choice, so which one I should to use? Actually what is the difference between IsAssignableFrom, IsInstanceOfType, and is operator?
You use whatever you have the information for.
If you have an instance and a static type you want to check against, use is.
If you don't have the static type, you just have a Type object, but you have an instance you want to check, use IsInstanceOfType.
If you don't have an instance and you just want to check the compatibility between a theoretical instance of a Type and another Type, use IsAssignableFrom.
But really is seems like you are just re-implementing the as operator (except that yours would also work for non-nullable value types, which is usually not a big limitation).
I guess you're effectively implementing a version of the as operator that works with value types as well as reference types.
I'd go for:
public static T SafeCastAs<T>(this object obj)
{
return (obj is T) ? (T) obj : default(T);
}
IsAssignableFrom works with types, and is works with instances. They will give you the same results in your case, so you should use the simplest version IMHO.
As for IsInstanceOfType:That is implemented in terms of IsAssignableFrom, so there will be no difference.
You can prove that by using Reflector to look at the definition of IsInstanceOfType():
public virtual bool IsInstanceOfType(object o)
{
if (o == null)
{
return false;
}
return this.IsAssignableFrom(o.GetType());
}
I guess you should just go with "as" instead of your custom "SafeCastAs". But this will work only for classes (not structs), so if you want to use this method for structs as well I can get it.
Operator "is" basically gives you the same as Type.IsAssignableFrom, so you can keep only "is", it checks if you can safely cast obj to T, without exceptions. So it will cover both previous checks in your method. But you should be aware about that it does not check if you can assign obj to T, because of user defined conversions: explicit and implicit keywords.
These functions and operators have different meaning. If you have objects you can always get types. so you do not work on what you have, but you do what needs to be done.
When you are working with a class hierarchy differences are very clear.
Look at following example
class ABase
{
}
class BSubclass : ABase
{
}
ABase aBaseObj = new ABase();
BSubclass bSubclassObj = new BSubclass();
ABase subObjInBaseRef = new BSubclass();
Different operations yield different results.
typeof(ABase).IsInstanceOfType(aBaseObj) = True
typeof(ABase).IsInstanceOfType(bSubclassObj) = True
typeof(ABase).IsInstanceOfType(bSubclassObj) = True
typeof(BSubclass).IsInstanceOfType(aBaseObj) = False
bSubclassObj is ABase = True
aBaseObj is BSubclass = False
subObjInBaseRef is BSubclass = True
subObjInBaseRef is BSubclass = True
typeof(ABase).IsAssignableFrom(typeof(BSubclass)) = True
typeof(BSubclass).IsAssignableFrom(typeof(ABase))= False
In case on no hierarchy, may be everything was same.
But if you work with hierarchy, IsAssignableFrom , is , and IsInstanceOfType yield different results.
There are more possible combinations that could be tried. For example, you can introduce a class C which is not related in anyway with existing classes in this example.
I think I'm having boxing issues
foreach(var p in item.GetType().GetProperties().
Where(p => p.GetValue(original, null) is ValueType))
{
var originalValue = p.GetValue(original, null);
var modifiedValue = p.GetValue(item, null);
if (!originalValue.Equals(modifiedValue))
kvpData.AppendFormat("{0}={1}&", p.Name, originalValue);
}
originalValue is never equal to modifiedValue, Im guessing it's because they are boxed inside Object. But how do I fix it?
It is not a boxing issue. Equals is a virtual method, which the boxed value types override just fine.
However, I am not sure what the issue is. Could it be that there aren’t actually any matching properties? Remember that GetProperties() without any parameters will only return public properties. If the properties you need are private, you need to add some BindingFlags:
GetProperties(BindingFlags.Instance | BindingFlags.Public | BindingFlags.NonPublic)
(I’m assuming here that you don’t want static properties.)
Are you also sure that it is actually properties that you are after and not fields? Remember that if you declare something as
public string Name;
then it’s a field, whereas
public string Name { get; set; }
is a property. If it’s actually fields you need, you need to use GetFields() instead of GetProperties() with the same binding flags.
Linq is a great tool, but I'm not sure why you're using it here. You're actually causing the set of properties to get iterated twice, which is very expensive. I'd write the code without Linq. Also, there's no need to get the value more than once, which is again very expensive. Give this code a try. It avoids the flaws I pointed out and was comparing correctly when I made and tested a dummy class with it:
foreach(PropertyInfo p in item.GetType().GetProperties())
{
if (p.PropertyType.BaseType == typeof(ValueType) || p.PropertyType == typeof(string))
{
var originalValue = p.GetValue(original, null);
var modifiedValue = p.GetValue(item, null);
if (originalValue != modifiedValue) kvpData.AppendFormat("{0}={1}&", p.Name, originalValue);
}
}
Also, note that strings are not a ValueType although they do implement a value comparison.
From MSDN: Object.Equals :
The default implementation of Equals supports reference equality for
reference types, and bitwise equality for value types. Reference
equality means the object references that are compared refer to the
same object. Bitwise equality means the objects that are compared have
the same binary representation.
That means that in your case this 2 objects (if they are reference types) are never point to the same instance.
There is no an easy way to resolve this in so generic way as you would like to do.
But you can implement (just an example) an IComparable on those types you gonna to compare, and after in this iteration check if the type of the value rturned implements that interface, so cast and call it's implemented IComparable.CompareTo method.
You can check if the object implements specified interfacce, in this current case IComparable, you can do something like this:
originalValue .GetType().GetInterfaces().Any(x =>
x.IsGenericType && x.GetGenericTypeDefinition() == typeof(IComparable))
Hope this helps.
I have a list defined as:
var Items = new List<IItem>();
Now there are a number of different classes that have that interface and one of them is Consumable. The Consumable class also has the == operator overloaded. Now I have the following code and is not working:
if(item1 == item2)
{
//code...
}
This does not work. I put a break point in the == operator overload and it never gets to it. When I go through line-by-line debugging, both item1 and item2 are of type Consumable, both GetType returns Consumable. I even try this code:
var temp = item1.GetType();
var temp2 = item2.GetType();
if (temp == temp2)
{
//code...
}
and this equality results is true. Now if I try this:
if(((Consumable)item1) == ((Consumable)item2))
{
//code...
}
and this triggers the break point in the == operator overload. Why would I have to manually cast the variable if when line-by-line debugging show it already thinks they are both consumable? Is it because I am pulling them from a list of IItems?
Since your list is List<IItem>, I'm assuming you have something like:
var item1 = Items[0];
or whatever; here item1 the variable is typed as IItem. Operator resolution happens during build via static analysis (not at runtime via polymorphism/RTTI), so the only == available is the default for any object, i.e. reference equality.
To support your custom operator, the variables must be typed accordingly, for example:
Consumable item1 = ..., item2 = ...;
Your cast achieves a similar thing.
Another option would be to make sure that == and Equals (and GetHashCode()) are in agreement, and use:
if(Equals(item1, item2)) {...}
which will do null checks and then use your overridden Equals method. This then supports polymorphism, so it doesn't matter what the types are.
The common langauge runtime does only know that your two objects implement the interface IItem. The smallest common part in object hierarchy is System.Object. And you did not overload the == operator of System.Object.
To use the correct overload you have to state the type of the object.
== does not check type equality, but for classes it checks reference equality. So if the variables are pointing to the same object it will be true. For example like this:
var temp = item1;
var temp2 = item1;
if( temp == temp2 )
{
//this code will execute
}
Shouldn't be the IItem IComparable?