Compare String and Object in C#

Compare String and Object in C#

In each case, the second operand of == is x, which is of type object. That means you're using the normal reference equality operator.

Now in your first case, you're using two string constants with the same contents. The C# compiler will use a single object for those two references. In the second case, x and y refer to distinct string objects with the same contents. The two references will be different, so == will return false.

You can fix the comparison by:

  • Use Equals instead - that's overridden by string (as opposed to the == operator which is only overloaded:

    Console.WriteLine(y.Equals(x)); // or x.Equals(y), or Equals(y, x)

    The use of the static Equals(object, object) method can be useful if either of the arguments can be null; it means you don't need to worry about a NullReferenceException.

  • Make both variables of type string, at which point the == overload within string will be picked at compile-time, and that overload compares the contents of the strings, not just the references

It's worth noting that it's not just a matter of the string literals itself being noticed by the C# compiler - it's about compile-time constant expressions. So for example:

object x = "mehdi emrani";
string y = "mehdi " + "emrani";
Console.WriteLine(y == x); // True

Here y is initialized using two string literals which aren't the same as the one used to initialize x, but the string concatenation is performed by the compiler, which realizes it's the same string it's already used for x.

Comparing C# object

A quick and dirty check of memory addressing helps to shine light on this using this (grabbed from here):

public static IntPtr GetAddress(object o)
{
unsafe
{
TypedReference tr = __makeref(o);
IntPtr ptr = **(IntPtr**)(&tr);
return ptr;
}
}

You can then do this:

Console.WriteLine(GetAddress(str1));
Console.WriteLine(GetAddress(str2));
Console.WriteLine(GetAddress(int1));
Console.WriteLine(GetAddress(int2));

I get:

35839968
35839968
35840128
35840152

What you should see is that the address of str1 and str2 are identical, and the address of int1 and int2 are different. This is because your strings are interned (because they are string literals). A single copy of the string shahrooz is stored in memory and both str1 and str2 are referring to it. Your int1 and int2 on the other hand are being boxed which is taking the value type int and placing it inside an object. Each int (or any other value type) boxed gets put in its own box with its own address.

Now the default behavior of == when given two reference types (like object) is to just compare the memory address (to see if they refer to the same object), and this is what's happening. In the case of the strings, it's the same object. In the case of the ints, it isn't.

As others have suggested, using Equals will give the desired result because Object.Equals is virtual and can be overridden, hence calling the appropriate int.Equals when given int and string.Equals when given a string.

Comparing two objects with == operator

I am not sure why it changes when you change it from 10 to 1

I believe this is an implementation detail and you should not rely on it (will try to find something in the specs) but some positive single digit numbers are cached in int.ToString implementation for .NET Core. Here is excerpt from UInt32ToDecStr which is called internally by int.ToString:

// For single-digit values that are very common, especially 0 and 1, just return cached strings.
if (bufferLength == 1)
{
return s_singleDigitStringCache[value];
}

As for equality - please check:

  1. C# difference between == and Equals().
  2. String interning in .Net Framework. (compiler will intern string literals, so all of them will point to the same address in memory)
  3. Using type dynamic

UPD:

Was not able to find anything in specs, but next code behaves differently in .NET Framework and .NET 6 (former one prints 11 times False and the latter prints 10 times True and one False):

var dict = new Dictionary<int, string>()
{
{0, "0"},
{1, "1"},
{2, "2"},
{3, "3"},
{4, "4"},
{5, "5"},
{6, "6"},
{7, "7"},
{8, "8"},
{9, "9"},
{10, "10"},
};

foreach(var kvp in dict)
{
Console.WriteLine(object.ReferenceEquals(kvp.Key.ToString(), kvp.Value));
}

UPD2:

The caching was introduced for performance reasons by this PR and is mentioned in Performance Improvements in .NET Core 3.0 blogpost:

In some sizeable web applications, we found that a large number of strings on the managed heap were simple integral values like “0” and “1”. And since the fastest code is code you don’t need to execute at all, why bother allocating and formatting these small numbers over and over when we can instead just cache and reuse the results (effectively our own string interning pool)? That’s what PR dotnet/coreclr#18383 does, creating a small, specialized cache of the strings for “0” through “9”, and any time we now find ourselves formatting a single-digit integer primitive, we instead just grab the relevant string from this cache.

private int _digit = 4;

[Benchmark]
public string SingleDigitToString() => _digit.ToString();










































MethodToolchainMeanErrorStdDevRatioGen 0Gen 1Gen 2Allocated
SingleDigitToStringnetcoreapp2.117.72 ns0.3273 ns0.3061 ns1.000.015232 B
SingleDigitToStringnetcoreapp3.011.57 ns0.1750 ns0.1551 ns0.65

Can´t compare string from COM-object with string-literal

The minimal reproducible example would be

object o = "mymy";
string s = "my";
Console.WriteLine(o == s+s);

This gives you the compiler warning CS0252, which basically says that you get the equality comparison of object and not the equality comparison of string.

string changes the meaning of == to that of .Equals(), which tests for character by character equality.

Using == or Equals for string comparison

I wouldn't use:

aa.Equals(bb)

unless I knew aa couldn't possibly be null. I might use:

string.Equals(aa,bb)

But I'd mainly use that it I wanted to use one of the specific StringComparison modes (invariant, ordinal, case-insensitive, etc). Although I might also use the StringComparer implementations, since they are a bit easier to abstract (for example, to pass into a Dictionary<string, Foo> for a case-insensitive ordinal dictionary). For general purpose usage,

a == b

is fine.

C# difference between == and Equals()

When == is used on an expression of type object, it'll resolve to System.Object.ReferenceEquals.

Equals is just a virtual method and behaves as such, so the overridden version will be used (which, for string type compares the contents).

Difference when comparing int and string having dynamic, object and int/string as parameter?

This is likely because Strings are immutable. The code that's making this happen is in the String class. It looks like this:

public static bool operator ==(string a, string b)
{
return string.Equals(a, b);
}

So any "==" comparisons are going to check equality rather than references on Strings.

Comparison between strings in c#

Objects can be compared relative to their reference in the heap. Most objects wouldn't be human-friendly to compare if this is how they were natively used, and so things like string actually implement an overload of the equality operators to be more intuitive. string compares equality (via the equality == operator) by first checking the memory reference (by calling object.ReferenceEquals(object, object) first) and if not the same reference, then falls back to comparing characters in the string, regardless of memory location.

String literals such as "somethingNew" are compiled to a variable with a reference to that string value in what .NET calls the intern memory pool... It's a means by which all strings with the same value (meaning same case and characters) all serve as pointers to a single reference in the intern pool rather than each having their own memory allocation for an identical value. This saves memory at the cost of looking up the value in the intern pool. This works because strings are immutable (read-only), so changing the value of a string via concatenation with the + or += operators or otherwise actually creates an entirely new string. String variables are not interned by default unless they are literals.

The comparison of strings in your example will succeed on the initial string equality object reference check and return true without any further analysis of equality. This will happen because your variables are string literals and thus interned (have the same memory address). If they were not interned, the comparison would fall back to character comparison, again regardless of memory location.

You can intern non-literal strings manually by using string.Intern(string)



Related Topics



Leave a reply



Submit