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 bystring
(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 aNullReferenceException
.Make both variables of type
string
, at which point the==
overload withinstring
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:
- C# difference between == and Equals().
- String interning in .Net Framework. (compiler will intern string literals, so all of them will point to the same address in memory)
- 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();
Method | Toolchain | Mean | Error | StdDev | Ratio | Gen 0 | Gen 1 | Gen 2 | Allocated |
---|---|---|---|---|---|---|---|---|---|
SingleDigitToString | netcoreapp2.1 | 17.72 ns | 0.3273 ns | 0.3061 ns | 1.00 | 0.0152 | – | – | 32 B |
SingleDigitToString | netcoreapp3.0 | 11.57 ns | 0.1750 ns | 0.1551 ns | 0.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
C# Creating an Unknown Generic Type at Runtime
How to Get a Combination of Keys in C#
Executing Query with Parameters
Windows Forms Graphic Issue on Windows 10 Os
Func Delegate with Ref Variable
Interesting "Params of Ref" Feature, Any Workarounds
ASP.NET How to Stream File to User
How to Access Ssis Package Variables Inside Script Component
Selectively Use Default JSON Converter
Unity - Checking If the Player Is Grounded Not Working
How to Bind Datatable to Datagridview in C#
Get Values from Process Standardoutput
How to Optionally Turn Off the JSONignore Attribute at Runtime
Start May Not Be Called on a Promise-Style Task. Exception Is Coming