.NET == and .Equals()

Equality might look like a simple concept at a first glance, but looking deeper it isn’t. In C# objects can be compared with the == operator, with the Equals(Object) member, with the Object.Equals(Object, Object) method or using custom comparators that implement one of or more of the IEquatable<T>, IComparable, IStructuralEquatable or IStructuralComparable interfaces. There’s also a Object.ReferenceEquals(Object, Object) method that can be used. In this post, we’ll take a closer look at the basics: == and .Equals().

Plain Vanilla Operator ==

The most common way to compare objects in C# is to use the == operator.

For predefined value types, the equality operator (==) returns true if the values of its operands are equal, false otherwise. For reference types other than string, == returns true if its two operands refer to the same object. For the string type, == compares the values of the strings.

Looking first at simple value types, this makes sense and makes comparisons of e.g. integers behave logical. Looking at more complex value types such as DateTime it also makes sense. If we put the current date in two variables we expect them to be equal.

var d1 = DateTime.Now.Date;
var d2 = DateTime.Now.Date;
 
Console.WriteLine(d1 == d2); // Writes True

Reference types are handled differently; == by default compares if the two variable are references to the same object. The contents of the object doesn’t matter.

var sb1 = new StringBuilder("Blue");
var sb2 = new StringBuilder("Blue");
 
Console.WriteLine(sb1 == sb2);

The string type is an exception pointed out in the documentation. It is a reference type stored on the heap, but everything possible has been done to make it behave like a value type. It is immutable. == compares the contents of the strings.

But string is not the only one; looking just in the System namespace the classes Uri and Version compares the content instead of checking if the variables reference the same object. It’s possible to test by comparing the output of == to that of Object.ReferenceEquals(Object, Object). The latter checks if the two references are to the same object or to different objects.

// Strings are highly optimized to share storage space. Using a StringBuilder is
// a way to get two different string instances with the same value.
var s1 = "Blue";
var sb = new StringBuilder("Bl");
sb.Append("ue");
var s2 = sb.ToString();
 
Console.WriteLine(s1 == s2); // True
Console.WriteLine(object.ReferenceEquals(s1, s2)); // False
 
var u1 = new Uri("http://localhost");
var u2 = new Uri("http://localhost");
Console.WriteLine(u1 == u2);  // True
Console.WriteLine(object.ReferenceEquals(u1, u2)); // False
 
var v1 = new Version(1, 2, 3);
var v2 = new Version(1, 2, 3);
Console.WriteLine(v1 == v2); // True
Console.WriteLine(object.ReferenceEquals(v1, v2)); // False

Overloaded Operator ==

For string, Uri and Version the default implementation of == is obviously not used, but instead a more specific overload is provided by the framework. In fact, all of them override the == operator by implementing the public static bool operator ==.

Note that the operator method is static. It isn’t an instance member. It isn’t virtual. The decision to use it or not will be done entirely at compile time. If the references are cast to another type, such as object the custom operator won’t be used. It’s enough to cast one of the operands to object to get the default reference comparison. Using the strings from the previous example, we’ll treat one of them as an object

object o1 = s1;
Console.WriteLine(o1 == s2); // False
Console.WriteLine(s2 == o1); // False

Compiling the code will give a warning: Possible unintended reference comparison; to get a value comparison, cast the left hand side to type ‘string’.

The verdict for == is that it behaves consistent until inheritance is involved. Since it is resolved at compile time it simply can’t deal with inheritance. So == will be a reasonable default for the 90%+ of cases in a program where no inheritance is involved and the compile time type of the references is the same as the run time type. For the other few percent of comparisons, something more powerful is needed.

.Equals(Object)

When the dynamic type of the objects need to be taken into consideration, the .Equals(Object) method can be used. It is virtual and allows each class to define it’s own behaviour. Adjusting the code above to use Equals shows the difference.

Console.WriteLine(s2.Equals(o1)); // True
Console.WriteLine(o1.Equals(s2)); // True

The method is virtual so in both cases, an overload of .Equals() on String will be called. But, the overload resolution is done on the static (i.e. compile time) type. Which means that in one case String.Equals(Object) will be called and in the second case String.Equals(String). The only difference between them is that the former has to cast the parameter, which is a small performance penalty. Providing a specialized overload with the right type can give some performance improvements, so for library code like String it’s a good idea to provide that overload.

The IEquatable<T> Interface

All types inherits the .Equals(Object) method from Object, so it can be used on any type in the .NET framework. In some cases it also makes sense to mark a type as implementing the a more specific version of .Equals(), comparing to the right type. That is exactly what the IEquatable<T> interface does.

public interface IEquatable<T>
{
  bool Equals(T other);
}

With that, it might be tempting to wrap up this post and declare it done. But there are a few important details to add, regarding consitency.

Consistency

The first observation regarding consistency is that for non-virtual calls, the basic mathematical requirements of an equivalence relation should hold:

  • a == a and a.Equals(a) should always be true (Reflexivity).
  • a == b, b == a, a.Equals(b) and b.Equals(a) should always give the same result. (Symmetry)
  • If a == b is true and b == c is true, then a == c should also be true (Transitivity). The same applies to a.Equals(b), b.Equals(c) and a.Equals(c).

There is also one more important part of consistency that must be dealt with, at least if the class will ever be used in a Dictionary<TKey, TValue>: GetHashCode(). A dictionary works by first grouping item in buckets using the Object.GetHashCode() virtual method. Then it ensures that it has found the right item by checking equality (by calling .Equals() unless a custom comparer is provided). That means that if two objects are considered equal, but gives different hash codes, the Dictionary<TKey, TValue> behave peculiar. Let’s have some fun and try!

struct Person
{
  public int Age { get; set; }
  public string Name { get; set; }
 
  // A person is uniquely identified by name, so let's use it for equality.
  public override bool Equals(Object obj)
  {
    return (obj is Person) && ((Person)obj).Name == Name;
  }
 
  // For lazyness reasons we (incorrectly) use the age as the hash code.
  public override int GetHashCode()
  {
    return Age;
  }
}

The Person class is clearly incorrectly implemented as Equals() and GetHashCode won’t behave consistently. If we use Person as the key to a dictionary we can get some “fun” results.

var favColours = new Dictionary<Person, string>();
 
var p = new Person()
{
  Age = 1,
  Name = "Alice"
};
 
favColours[p] = "Blue";
 
// Happy birthday Alice!
p.Age = 2;
favColours[p] = "Green";
 
Console.WriteLine(favColours.Count); // 2
 
var keys = favColours.Keys.ToArray();
Console.WriteLine(object.ReferenceEquals(keys[0], keys[1])); // True

The output of that snippet of code shows we have two person objects (being a struct, a copy is made when stored in the dictionary) that are used as keys. They have resulted in different entries in the dictionary – but comparing them they are equal. That’s confusing. Don’t go there.

When implementing custom equality, three different methods should always be implemented and behave consistently.

  • Make an overload for the == operator.
  • Override .Equals(Object) and optionally provide an optimized .Equals(MyType).
  • Override .GetHashCode() and make sure that it returns the same hash code for all objects that compares are equal.

That’s all for now regarding equality; in my next post I’ll have a look at comparisons with IComparable.

Configure XML formatting in Notepad++

This is a guest post from Albin Sunnanbo, sharing a convenient config trick for Notepad++.

If you are using Notepad++ as your favorite editor for text files you should configure style mapping for XML files.
Instead of going into the menu Language->Xml everytime you open a file with xml data you can configure XML as default language for certain file extensions.

First go into the menu and select Settings->Style Configurator
Notepad++ style configurator menu

Select XML in the left list and in the small textbox User ext. enter your most frequently used file extensions.
For c# development use at least “config csproj sln”.
Notepad++ style configurator dialog
Now XML syntax high lightning will be enabled for those file extensions the next time you open them.

Code Smell:System.Tuple

System.Tuple is a set a generic utility classes that lets a lazy developer get away without creating a separate class. At first sight it might look handy, but it isn’t. It’s a code smell. And Tuples behave the same as anything that smells: It gets worse if you leave it and let them spread throughout the code base.

Typical use of tuples include allowing multiple return values for functions or storing temporary data inside a method.

/// <summary>
/// Recommended pressure of front and back tires of the car.
/// </summary>
public Tuple<double, double> TirePressure
{ get { return Tuple.Create(2.0, 2.4); } }
 
/// <summary>
/// Calculate the average of numbers in a sequence of sequences.
/// </summary>
/// <param name="values"></param>
/// <returns></returns>
public int AggregatedAverage(IEnumerable<IEnumerable<int>> values)
{
    var firstLevel = values.Select(s => Tuple.Create(s.Average(), s.Count()));
 
    return firstLevel.Select(fl => fl.Item1 * fl.Item2).Sum() / firstLevel.Sum(fl => fl.Item2);
}

Neither of these make the code very readable, so what should be done instead?

Posted in C# on 2014-08-28

A Geek’s Nightmare

Last night I woke up after a night mare. A nightmare containing a future, “improved” version of powershell a competing blogger and Entity Framework Migrations. Slightly off topic, but I’ll share it anyway.

I am back at NDC in Oslo as a speaker. Not this year’s NDC, but next year’s. The room is already filled up when I enter, opening my laptop while walking to get the presentation going in the few minutes I have left before the official start time. The computer is on, I just have to open the presentation. Going into my dropbox folder to get it, the explorer window slowly renders. After 30 seconds the menu bar is in place. Another 30 seconds and the first folder is shown. When I finally see the folder I need and click it, everything just grinds to a halt.

Whatever. I’m a hacker. I bring up the command prompt. But this is not the ordinary command prompt. This is a new, “improved” command prompt inspired by powershell that not only lists the file but actually reads them to be able to show some info about the contents of the file in the directory listing. The first file in the directory is a multi gigabyte presentation with a lot of images, so the shell window just freezes and won’t let me reach my precious presentation file. It’s now seven minutes past start time. I’m sweating.

Desperately I start talking, trying to get the message through without any slides to help. (I think it was an advanced EF Migrations talk). A famous blogger sitting in the audience tries to help and I’ve just found out that our sites are ranked the same on Alexa, so it’s fine. There’s a whiteboard in the room so I can write code on that instead of using the computer if I need. I’m twelve minutes late.

While I’m desperately doing a last attempt at getting my slide deck running people are starting to leave the room. The famous blogger has had enough, he leaves too. I feel devastated, but then I get the presentation running. People are actually coming back into the room, bringing in some of their friends. More people are joining, having tried other talks first but changed their minds.

At the end, the room is packed. People are sitting on the floor. The talk is a success.

Now, does anybody have any idea of what all this means?

Posted in Other on 2014-08-28

Kentor.AuthServices SAML2 Owin Middleware Released

I just pushed the first version of our Owin SAML2 middleware to nuget and github as part of Kentor.AuthServices 0.5.2. Kentor.AuthServices is a SAML2 Service Provider implementation for ASP.NET, offering an HTTP Module, drop in MVC controllers and (now) an Owin middleware. The intention with the library is to approach SAML2 from a .NET perspective, making a component that fits seamlessly into the existing .NET security infrastructure.

Kentor.AuthServices is now made up of four main packages.

  • The core Kentor.AuthServices library which contains all the SAML2 functionality and the classic http module. The library exposes a public API for working with SAML2 authentication requests and responses, that can be used to e.g. build an identity provider.
  • The Kentor.AuthServices.Mvc library which is a drop in MVC controller that enables SAML2 authentication to an MVC application without writing a single line of code. Just install the nuget package and add the needed configuration to web.config to get going.
  • The Owin middleware that offers SAML2 authentication in the same way as existing providers for external authentication (e.g. Google, Facebook) and integrates with ASP.NET Identity.
  • The Stub idp for testing, that is available for free and answers to any incoming AuthnRequests.

All the source is hosted in a GitHub repo, including sample applications.

What’s new in 0.5.2?

There are three major improvements in 0.5.2 over previous versions.

  • Basic Service Provider metadata with the data that is mandatory according to the SAML2 specification is generated and published.
  • There is now support for one instance of AuthServices to work with multiple identity providers. This is a first step on the road to full federation support.
  • We’ve created a brand new owin middleware that is compatible with ASP.NET Identity and works the same way as existing external authentication providers such as Google and Facebook.

For the full list of implemented features, please see the GitHub Milestone for 0.5.0

Software Development is a Job – Coding is a Passion

I'm Anders Abel, a systems architect and developer working for Kentor in Stockholm, Sweden.

profile for Anders Abel at Stack Overflow, Q&A for professional and enthusiast programmers

The complete code for all posts is available on GitHub.

Popular Posts

Archives

Series

Powered by WordPress with the Passion for Coding theme.