This is super dumb, but I've googled and checked references and I just cannot find an answer... Why can an int or float etc be added as part of a string without converstion but not on it's own? that is:
while this work fine:
string st = "" + 12;
this doesn't (of course):
string st = 12;
Where is the magic here? I know it works I just want to know WHY it works and how I control HOW the conversion is done?
In the first statement, the left operand for the + is a string, and as such + becomes the concatenation operator. The compiler finds the overload for that operator which takes a string and an arbitrary value as operands. This converts both operands to strings (using ToString()) then joins them.
The second statement does not work because there's no implicit cast from int to string.
You can control how the conversion is done by using parentheses to change the order of operations (semi-effective) or by writing code to handle the conversions pre-emptively.
This is how string concatenation is designed to work, and as BoltClock's answer noted, the compiler is using the + as the string concatenation operator. From the C# Language Specification on string concatenation, we find that:
Any non-string argument is converted to its string representation by invoking the virtual ToString method inherited from type object.
String concats in .NET ultimately resolve to calls to one of the overloads of the static String.Concat methods. This is an optimization to reduce the number of temporary strings that would otherwise be created when mutliple concatenations occur in a single statement.
In short the reason this works is because a number of the String.Concat overloads will accept object in the argument list and since an int, float etc. are in essence objects they can be passed to the Concat overload that accepts one or more object parameters. Internally Concat of basically does a .ToString() on the incomming object therefore turning your int into it's string representation.
In your specific example
string st = "" + 12;
The compiler will reconize that the first string is empty and simply call the String.Concat(object) overload. Which will convert the integer 12 to a string and assign it to st.
This overload is called because the integer can be implicitly boxed to fit into the object type and therefore satisfy the method overload selection.
Because the compiler will call .ToString() on all objects if one of the parameters is a string.
This is working because of the operator overload of the operator + on the string datatype.
Because the first is an expression and there C# makes an implicit conversion.
The second is an assignement with a static value, and the static value has no methods to be called.
As long as the value to be asigned is a variable or expression return there will be a toSting method to call.
It's because the compiler translates the addition to a call to String.Concat().
Internally, all operands are boxed (if necessary) and passed to the String.Concat method (which of course calls ToString() on all arguments).
Related
I created a .net extension method that converts an string to an integer and throws an InvalidCastException if the passed string isn't an integer:
public static int ToInt32(this String str)
{
int result;
// string is not a valid 32 bit integer
if (!Int32.TryParse(str, out result))
throw new InvalidCastException();
return result;
}
Is it possible do don't show up / offer the method on the string object if it isn't an integer?
Is it possible do don't show up / offer the method on the string object if it isn't an integer?
No. Even if the parameter were a literal, the C# language doesn't have a mechanism for restricting method overloads according to parameter values. And of course, there's no requirement you can make that the method argument be provided as a literal. A variable or other expression used to provide the argument could be literally any string, and the compiler has no way to identify the nature of the argument (i.e. check whether it could be parsed as an integer).
Depending on what you're trying to do, you might be able to solve the broader problem with a Roslyn code analyzer. That's a whole other "ball of wax" though.
Consider the following piece of code.
object str = new string(new char[] { 't', 'e', 's', 't' });
object str1 = new string(new char[] { 't', 'e', 's', 't' });
Console.WriteLine(str==str1); // false
Console.WriteLine(str.Equals(str1)); // true
I understand the equality operator working here that as we have implicitly casted to object, the equality operator is checking the references of both if they are equal and returns false.
But i am confused on the second one, returning true looks like it is calling Equals override implementation provided by the String type and it checks for content of string if they are equal.
My question is why it doesn't check for content equality for operator as well, their actual type is string not object. right ?
while the follwing code outputs ture for both:
object str = "test";
object str1 = "test";
Console.WriteLine(str==str1); // true
Console.WriteLine(str.Equals(str1)); // true
With:
Console.WriteLine(str==str1); // false
it is determined at compile-time which C# pre-defined (formal) overload of operator == to use. Since str and str1 are declared as object, the overload operator ==(object, object) is chosen. This is fixed at compile-time. Just because the actual run-time types happen to be more specific, that does not change. If you want binding at run-time, use Console.WriteLine((dynamic)str == (dynamic)str1); /* true */ instead.
With:
Console.WriteLine(str.Equals(str1)); // true
you call a virtual method on object. Virtual means it will go to whatever override is relevant at run-time. The class System.String has an override, and since str will have run-time type System.String, the override will be used by the "virtual dispatch".
Regarding the addition to the bottom of your question: That situation is different because of string interning. String interning is an optimization where the same physical instance is used for formally distinct strings whose values are identical. When you have two strings whose values are given in the source code, string interning will "optimize" and make two references to the same instance. This is usually harmless because strings are guaranteed to be immutable. So normally you do not care if it is the same instance or another instance with identical value. But in your example, we can "reveal" the interning.
Note: String interning was not relevant to your original question. Only after you added a new example to your question, string interning became relevant.
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).
This happens because of string interning; when you write:
object str = "test";
object str1 = "test";
Console.WriteLine(str==str1);
This works as expected as the two strings are internally and silently copied to one location by the compiler so the two pointers will actually point to the same object.
If you create a string from an array of chars, the compiler is not clever enough to understand your intention and that it is the equivalent of above, so, being a string a reference type, they're effectively two different objects in memory.
Have a look at this article: https://blogs.msdn.microsoft.com/ericlippert/2009/09/28/string-interning-and-string-empty/
The Equals method is overridden in string, therefore it's comparing the actual content of the string rather than the address as == (ReferenceEquals) does in your case as the type is object.
I believe it is because the String == operator only takes string types as parameters, while the .Equals method takes object types as parameters.
Since the string == only take string types as parameters, the overload resolution selects the object == operator to use for the comparison.
The help to String.Equals method is giving this as a remark:
This method performs an ordinal (case-sensitive and
culture-insensitive) comparison.
So, the comparison is done by checking the string char by char, thus giving true.
I noticed that in C# that the + operator takes a string on the left and an object on the right:
string output = "" + numbers.ElementAt(0);
//vs
string output = numbers.ElementAt(0).ToString();
Why is the default behavior to accept an object on the right and not a string? When I look other + operators for say int, it is strongly typed for int/int instead of int/object.
I know in C# all objects have the ToString() method, is this just a way to remove the need to call .ToString() in the first example above?
Why is the default behavior to accept an object on the right and not a string?
Well, first off, there is an overload that accepts two strings, and if you pass two strings that's the overload that will be used, so it is the default behavior. But if you pass a string and a non-string, then the (string, object) overload will be used.
As for why that overload is there, it's because the C# language designers felt that it was entirely sensible to concat a string to any object, and had a reasonable implementation for doing so for any type of object.
When I look other + operators for say int, it is strongly typed for int/int instead of int/object.
How would you implement a operator +(int, object) overload? I don't see any good implementation here, unlike with string concatenation.
I know in C# all objects have the ToString() method, is this just a way to remove the need to call .ToString() in the first example above?
It actually works if the object is null, whereas your solution does not. If you would account for that then yes, that is essentially all that the overload is doing.
is this just a way to remove the need to call .ToString() in the first example above
Yes.
String concatenation automatically transforms non-strings into strings.
Source: C# Language Specification: Addition operator, MSDN.
I have some code like:
int value = 5;
MessageBox.Show ( value );
and the MessageBox.Show complains saying:
"cannot convert from 'int' to
'string'"
I seem to remember some cases where values seem to be implicitly converted to string values, but can't recall them exactly.
What's the reason behind this decision that any value isn't implicitly convertible to string values?
MessageBox.Show() only accepts a string. When you use something like Debug.WriteLine, it accepts a bunch of different object types, including object, and then calls ToString() on that object. This is probably what you're experiencing.
A short solution (everywhere you need a string):
MessageBox.Show(""+value);
But I would prefer a ToString() or a String.Format() in most cases.
To answer the "Why" part: because implicit conversions can be dangerous and can undermine type-safety.
"1" + 2 = "12" = 12, not always what you want or expect.
For the exact reason, you would have to ask either the C# compiler guys, or one of the .NET runtime guys.
However, there are no places in the .NET framework or the C# language where values are automatically and implicitly convertible to strings.
You might, however, think of the way string concatenation works, but that only works because there are a lot of overloads on the string.Concat method, including one that takes an object.
In other words, this is allowed:
string s = "Hello there: " + 4;
Other methods around in the framework also has lots of overloads, such as Debug.WriteLine and such, where it will easily accept your integer or decimal, convert it to a string through a call to .ToString, and then print it.
It isn't, however, something built into string or int, but the method itself.
Try
MessageBox.Show(value.ToString());
This could be a very basic question for prodigies.But I have a doubt to handle it.
During Conversion we are using :
int.Parse(someThing)
Convert.ToInt32 or Convert.ToString()....
(Int32)(someValue)
even we use "as"
What are the restrictions for using each?
int.Parse assumes a string as a parameter and as such is only suitable for converting raw string representations to integers
Convert.ToInt32() will attempt to convert pretty much any object to an integer representation. Where the representation isn't a valid int (i.e. using a float with the value 55.3 or a string containing a word) this will raise a FormatException. If the integer is too large to fit in an int then an OverflowException will occur.
(int) is a direct cast. It's basically saying "I know that this object is really an integer, treat it as such". If the object actually isnt an integer you'll get an invalid cast exception.
Finally, as behaves the same as a direct cast except where the object is not of the correct type it will assign null. Not sure how this applies to int with it being a non-nullable type but certainly usable with
int? myInt = someVar as int?;
Convert.ToNnnn has the restrictions stipulated by its overloads; for instance, you cannot call Convert.ToMyCustomType (since that method does not exist). You can pass pretty much anything into Convert.ToString (or Convert.ToInt32 or any other of the Convert.ToNnnn methods) but the result may not always make sense. Also, if the method fails to perform the conversion it may throw an InvalidCastException.
The regular type case ((int)someValue) can be used in all cases where there is an explicit conversion available. If you try to perform an illegal cast you will get an exception thrown at you.
The as keyword can be used to cast type to another (reference) type (and it will return null if it's not possible). The as keyword cannot be used with value types (such as Int32, Point or DateTime).
In my own code I typically use a mixture of them, but in general I follow the following scheme:
If I want to convert a string to a numeric type, I usually use the TryParse provided by that numeric type.
If I want to cast a class instance to a base class or interface, I usually use the as keyword.
In other cases I use regular type casting within try/catch blocks.
Convert.ToInt32 can convert from various types (DateTime, Decimal, etc) back to an integer, while int.Parse() only converts from a string.
If you're only parsing strings back to integers, Convert.ToInt32 is just an alias for int.Parse, so you might as well use int.Parse instead.
Regarding casting, you cannot cast a string to an integer, you have to convert it like mentioned above.
Consider that you are working with a database, and you have a column that is inefficiently declared as varchar while it stores integer data. You cannot do (int)dr["column"] when using a SqlDataReader for instance, you would have to use int.Parse(dr["column"].ToString()) instead. If the column was of the int column type in the database, you could then have used the cast: (int)dr["column"].