Q: Why does casting the same two numbers to Object make them not equal? I have following code snippet but I am getting wrong output.
class Program
    {
        static void Main(string[] args)
        {
            var i = 10000;
            var j = 10000;
            Console.WriteLine((object) i == (object) j);


        }
    }

I was expecting true but I am getting false

A: You are using == that by default invokes ReferenceEquals. You should use:
Console.WriteLine(((object)i).Equals((object)j));


A: You are boxing the numbers (via the object cast) which creates a new instance for each variable. The == operator for objects is based on object identity (also known as reference equality) and so you are seeing false (since the instances are not the same)
To correctly compare these objects you could use Object.Equals(i, j) or i.Equals(j). This will work because the actual runtime instance of the object will be Int32 whose Equals() method has the correct equality semantics for integers.

A: because you are comparing two differents objects, that the reason.

A: They are two different objects and will never be equal.  Their values are equal.

A: Operators are overloaded, not overridden. This means that the actual method that will be invoked is determined at compile time instead of at runtime. To understand this difference, you should think of it like this:
Suppose you have the following code
public class MyBase
{
    public virtual void DoSomething() { Console.WriteLine("Base"); }
}

public class A : MyBase
{
    public override void DoSomething() { Console.WriteLine("A"); }
}

public class B : MyBase
{
    public override void DoSomething() { Console.WriteLine("B"); }
}

Now you can create an instance of A and B:
MyBase a = new A();
MyBase b = new B();

a.DoSomething(); // prints "A"
b.DoSomething(); // prints "B"

What happens is that every instance of MyBase contains a hidden reference to a vtable that points to the actual implementation of DoSomething() that must be invoked. So when the runtime needs to invoke DoSomething() on a, it will look at the vtable of a and will find the version that prints "A". The important part is that this happens at runtime.
Now when you overload a method, you create a method with the same name, but with a different signature.
public class C
{
    public void Print(string str) { Console.WriteLine("string: " + string); }
    public void Print(int i) { Console.WriteLine("int: " + i); }
    public void Print(object obj) { Console.WriteLine("object: " + obj.ToString()); }
}

var c = new C();
c.Print("1"); // string: 1
c.Print(1); // int: 1
c.Print((object)1); // object: 1

This time the compiler decides which method is invoked instead of the runtime. And the compiler isn't very smart. When it sees (object)1, it sees a reference to an object, and the compiler tries to find a version of Print() that can take an object as argument.
Sometimes the compiler finds two versions that could be used. For example, "1" is a string, but it is also an object, so both the first and the third version of Print() are capable of accepting a string. In this case the compiler chooses the version that has the most specific argument types. Sometimes the compiler can't choose, like in the following example:
public static void Print(string a, object b) { Console.WriteLine("V1"); }
public static void Print(object a, string b) { Console.WriteLine("V2"); }

Print("a", "b"); // does this print "V1" or "V2"?

In this case, none of the versions is more specific than the other, so the compiler produces a compiler error: The call is ambiguous between the following methods or properties: 'Test.Program.Print(string, object)' and 'Test.Program.Print(object, string)'
As I said in the beginning, the == operator is overloaded. This means the compiler chooses which version to use. The most generic version is the one where both operands are of type Object. This version compares references. This is why (object) i == (object) j returns false.
