GetHashCode and Equals are implemented incorrectly in System.Attribute? - c#

Seeing from Artech's blog and then we had a discussion in the comments. Since that blog is written in Chinese only, I'm taking a brief explanation here. Code to reproduce:
[AttributeUsage(AttributeTargets.Class, Inherited = true, AllowMultiple = true)]
public abstract class BaseAttribute : Attribute
{
public string Name { get; set; }
}
public class FooAttribute : BaseAttribute { }
[Foo(Name = "A")]
[Foo(Name = "B")]
[Foo(Name = "C")]
public class Bar { }
//Main method
var attributes = typeof(Bar).GetCustomAttributes(true).OfType<FooAttribute>().ToList<FooAttribute>();
var getC = attributes.First(item => item.Name == "C");
attributes.Remove(getC);
attributes.ForEach(a => Console.WriteLine(a.Name));
The code gets all FooAttribute and removes the one whose name is "C". Obviously the output is "A" and "B"? If everything was going smoothly you wouldn't see this question. In fact you will get "AC" "BC" or even correct "AB" theoretically (I got AC on my machine, and the blog author got BC). The problem results from the implementation of GetHashCode/Equals in System.Attribute. A snippet of the implementation:
[SecuritySafeCritical]
public override int GetHashCode()
{
Type type = base.GetType();
//*****NOTICE*****
FieldInfo[] fields = type.GetFields(BindingFlags.NonPublic
| BindingFlags.Public
| BindingFlags.Instance);
object obj2 = null;
for (int i = 0; i < fields.Length; i++)
{
object obj3 = ((RtFieldInfo) fields[i]).InternalGetValue(this, false, false);
if ((obj3 != null) && !obj3.GetType().IsArray)
{
obj2 = obj3;
}
if (obj2 != null)
{
break;
}
}
if (obj2 != null)
{
return obj2.GetHashCode();
}
return type.GetHashCode();
}
It uses Type.GetFields so the properties inherited from base class are ignored, hence the equivalence of the three instances of FooAttribute (and then the Remove method takes one randomly). So the question is: is there any special reason for the implementation? Or it's just a bug?

A clear bug, no. A good idea, perhaps or perhaps not.
What does it mean for one thing to be equal to another? We could get quite philosophical, if we really wanted to.
Being only slightly philosophical, there are a few things that must hold:
Equality is reflexive: Identity entails equality. x.Equals(x) must hold.
Equality is symmetric. If x.Equals(y) then y.Equals(x) and if !x.Equals(y) then !y.Equals(x).
Equality is transitive. If x.Equals(y) and y.Equals(z) then x.Equals(z).
There's a few others, though only these can directly be reflected by the code for Equals() alone.
If an implementation of an override of object.Equals(object), IEquatable<T>.Equals(T), IEqualityComparer.Equals(object, object), IEqualityComparer<T>.Equals(T, T), == or of != does not meet the above, it's a clear bug.
The other method that reflects equality in .NET are object.GetHashCode(), IEqualityComparer.GetHashCode(object) and IEqualityComparer<T>.GetHashCode(T). Here there's the simple rule:
If a.Equals(b) then it must hold that a.GetHashCode() == b.GetHashCode(). The equivalent holds for IEqualityComparer and IEqualityComparer<T>.
If that doesn't hold, then again we've got a bug.
Beyond that, there are no over-all rules on what equality must mean. It depends on the semantics of the class provided by its own Equals() overrides or by those imposed upon it by an equality comparer. Of course, those semantics should either be blatantly obvious or else documented in the class or the equality comparer.
In all, how does an Equals and/or a GetHashCode have a bug:
If it fails to provide the reflexive, symmetric and transitive properties detailed above.
If the relationship between GetHashCode and Equals is not as above.
If it doesn't match its documented semantics.
If it throws an inappropriate exception.
If it wanders off into an infinite loop.
In practice, if it takes so long to return as to cripple things, though one could argue there's a theory vs. practice thing here.
With the overrides on Attribute, the equals does have the reflexive, symmetric and transitive properties, it's GetHashCode does match it, and the documentation for it's Equals override is:
This API supports the .NET Framework infrastructure and is not intended to be used directly from your code.
You can't really say your example disproves that!
Since the code you complain about doesn't fail on any of these points, it's not a bug.
There's a bug though in this code:
var attributes = typeof(Bar).GetCustomAttributes(true).OfType<FooAttribute>().ToList<FooAttribute>();
var getC = attributes.First(item => item.Name == "C");
attributes.Remove(getC);
You first ask for an item that fulfills a criteria, and then ask for one that is equal to it to be removed. There's no reason without examining the semantics of equality for the type in question to expect that getC would be removed.
What you should do is:
bool calledAlready;
attributes.RemoveAll(item => {
if(!calledAlready && item.Name == "C")
{
return calledAlready = true;
}
});
That is to say, we use a predicate that matches the first attribute with Name == "C" and no other.

Yep, a bug as others have already mentioned in the comments. I can suggest a few possible fixes:
Option 1, Don't use inheritence in the Attribute class, this will allow the default implementation to function. The other option is use a custom comparer to ensure you are using reference equality when removing the item. You can implement a comparer easily enough. Just use Object.ReferenceEquals for comparison and for your use you could use the type's hash code or use System.Runtime.CompilerServices.RuntimeHelpers.GetHashCode.
public sealed class ReferenceEqualityComparer<T> : IEqualityComparer<T>
{
bool IEqualityComparer<T>.Equals(T x, T y)
{
return Object.ReferenceEquals(x, y);
}
int IEqualityComparer<T>.GetHashCode(T obj)
{
return System.Runtime.CompilerServices.RuntimeHelpers.GetHashCode(obj);
}
}

Related

Is sql to Linq union by reference? (the same for except and intersect?)

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..

Cast property values back to original types for comparison with reflection

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
}

IsAssignableFrom, IsInstanceOfType and the is keyword, what is the difference?

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.

Comparing the value of a property in two instances

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.

Why is the == operator (defined for the concrete type) not used?

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?

Categories