Why Does String.Format Discriminate? [duplicate] - c#

This question already has answers here:
string.Format fails at runtime with array of integers
(7 answers)
Closed 9 years ago.
String.Format will happily work correctly with an array of strings, but fails when dealing with an array of ints with exception :
Index (zero based) must be greater than or equal to zero and less than
the size of the argument list.
string result = null;
var words = new string[] { "1", "2", "3" };
result = String.Format("Count {0}{1}{2}", words); //This works.
var nums = new int[] { 1, 2, 3 };
result = String.Format("Count {0}{1}{2}", nums); //This throws an exception.
Why is this so?

This happens because the string.Format overload you are using wants object[]. A string is a reference type, so string[] can be implicitly cast to object[], but int is a value type, and would have to be boxed before being put in an array of objects. So when you're using int it selects another overload that just takes one parameter, and then passes the entire int[] as a single object instead of passing each int by itself.

Because ToString() method is called for Array of ints. And it's becomes 1 object.
This code:
var nums = new int[] { 1, 2, 3 };
result = String.Format("Count {0}", nums);
Will result:
Count System.Int32[]

Related

Converting last index string to Index object in C#

We may access the last index of a list/array by the following:
var l = new List<string> {"1", "2a", "3cd"};
Console.WriteLine(l[^1]);
output: "3cd"
May I know if it is possible to cast the string ^1 to index object:
(second line is not working)
var s = "^1"
var index = (Index) s;
Console.WriteLine(l[index]);
To get the output: "3cd"
To instantiate an Index for one from the end, do this.
var index = new Index(1, true);
or
var index = Index.FromEnd(1);
var s = "^1"
just makes a string that uses the '^' char, that can be used in code, to indicate an Index from end.
There is no conversion, explicit or implicit between string and Index.
If, for some reason you wanted to read a Index struct from JSON, you could store both,
int value
and the optional
bool fromEnd = false
and use those to instantiate a new instance. This would be simpler than writing a parser for strings that contain index expressions.

How to change an array in a function without changing the original array? [duplicate]

This question already has answers here:
Copy Arrays to Array
(7 answers)
Closed 1 year ago.
In C# and many other languages, if you pass an array to a function it passes the pointer/reference which means you can change the value of an array from inside a function.
From Microsoft:
Arrays can be passed as arguments to method parameters. Because arrays are reference types, the method can change the value of the elements.
I have a special case where I need to access and change an array's contents from a function but I do not want to change the original array. I thought this would be quite simple. I could set a new array equal to the old array and change the new array. This acts the same, however, because the new array is just a pointer to the old one.
static void AddToArray(string[] array) {
var newArray = array;
newArray[2] = "y";
}
static void Main(string[] args) {
string[] array = new string[5];
array[0] = "h";
array[1] = "e";
AddToArray(array);
}
If you print the contents of array at each step:
"he"
"hey" (inside function)
"hey" (after function call)
I've done a lot of research online but somehow haven't found many other people who needed help with this. Advice is greatly appreciated!
You are not creating your array using "new" Keyword inside function. Change below line -
var newArray = array;
To
var newArray = new string[args.Length];
and after creating this as a new array, you can copy the values from args (passed) array

How do you sum only the integer elements in a list of objects in C# with a loop? [duplicate]

This question already has answers here:
When to use Cast() and Oftype() in Linq
(7 answers)
Closed 2 years ago.
Consider the following:
static void Main(string[] args)
{
List<object> someList = new List<object>();
someList.Add(1);
someList.Add(2);
someList.Add("apple");
someList.Add(true);
someList.Add(3);
}
The list items have not been defined with a data type and contains a boolean, a string, and an integer. Assuming there are many more list items of varying data types, how do I sum only the integers (such as 1, 2, and 3) from the list of objects using a simple loop or if statement? I'm just beginning in C#, thank you!
After searching, I've tried something like this, but with no success:
if(!someList.Contains(int))
List.Add(int);
If you know they are boxed int you could use OfType and Sum
var result = someList.OfType<int>().Sum();
Or for anything that is a representation of an int that would convert to a string representation, you could use Sum with ToString, and int.TryParse
var result = someList.Sum(x => int.TryParse(x.ToString(), out var value) ? value : 0);
Or if you like loops
var result = 0;
foreach (var value in someList.OfType<int>())
result += value;
// or
var result = 0;
foreach (var item in someList)
if (int.TryParse(item.ToString(), out var value))
result += value;
Additional Resources
Enumerable.OfType(IEnumerable) Method
Filters the elements of an IEnumerable based on a specified type.
Enumerable.Sum Method
Computes the sum of a sequence of numeric values.
Int32.TryParse Method
Converts the string representation of a number to its 32-bit signed
integer equivalent. A return value indicates whether the operation
succeeded.

How to sort Strings in C# [duplicate]

This question already has answers here:
C# sort Arraylist strings alphabetical and on length
(5 answers)
Closed 6 years ago.
I have to sort an array of strings. How can I do that, if:
They must be placed in order of string length.
If lengths are equal, the must be placed alphabetically.
Is there any simple to do that ?
Here's the traditional way in C# ...
static void Main(string[] args)
{
List<string> list = new List<string>();
list.Add("1991728819928891");
list.Add("0991728819928891");
list.Add("3991728819928891");
list.Add("2991728819928891");
list.Add("Hello");
list.Add("World");
list.Add("StackOverflow");
list.Sort(
delegate (string a, string b) {
int result = a.Length.CompareTo(b.Length);
if (result == 0 )
result = a.CompareTo(b);
return result;
}
);
Console.WriteLine(string.Join("\n", list.ToArray()));
}
Sample Output:
Hello
World
StackOverflow
0991728819928891
1991728819928891
2991728819928891
3991728819928891
You can do it with LINQ in the following way:
string[] arr = new[] { "aa", "b", "a" , "c", "ac" };
var res = arr.OrderBy(x => x.Length).ThenBy(x => x).ToArray();
Another way is to use Array.Sort with custom IComparer implementation.

string.Format fails at runtime with array of integers

Consider string.Format() whose parameters are a string and, among others in the overload list, an object[] or many objects.
This statement succeeds:
string foo = string.Format("{0} {1}", 5, 6);
as does this:
object[] myObjs = new object[] {8,9};
string baz = string.Format("{0} and {1}", myObjs;
as does an array of strings:
string[] myStrings = new string[] {"abc", "xyz"};
string baz = string.Format("{0} {1}", myStrings);
It seems that the integers, when specified individually, can be boxed or coerced to type object, which in turn is coerced to a string.
This statement fails at runtime.
int[] myInts = new int[] {8,9};
string bar = string.Format("{0} and {1}", myInts);
Index (zero based) must be greater than or equal to zero and less than the size of the argument list.
Why doesn't or can't the int array be coerced or boxed to an object[] or string[]?
Out of a small bit of curiosity, why doesn't the compiler catch this?
The call fails with the same reason the following will also fail:
string foo = string.Format("{0} {1}", 5);
You are specifying two arguments in the format but only specifying one object.
The compiler does not catch it because int[] is passed as an object which is a perfectly valid argument for the function.
Also note that array covariance does not work with value types so you cannot do:
object[] myInts = new int[] {8,9};
However you can get away with:
object[] myInts = new string[] { "8", "9" };
string bar = string.Format("{0} {1}", myInts);
which would work because you would be using the String.Format overload that accepts an object[].
Your call gets translated into this:
string foo = string.Format("{0} {1}", myInts.ToString());
which results in this string:
string foo = "System.Int32[] {1}";
So as the {1} doesn't have a parameter, it throws an exception
I think the concept you are having an issue with is why int[] isn't cast to object[]. Here's an example that shows why that would be bad
int[] myInts = new int[]{8,9};
object[] myObjs = (object[])myInts;
myObjs[0] = new object();
The problem is that we just added an object into a int array.
So what happens in your code is that myInts is cast to object and you don't have a second argument to fill in the {1}
Short way to make it work (not the most optimal though):
int[] myInts = new int[] { 8, 9 };
string[] myStrings = Array.ConvertAll(myInts, x => x.ToString());
// or using LINQ
// string[] myStrings = myInts.Select(x => x.ToString()).ToArray();
bar = string.Format("{0} and {1}", myStrings);
This is quite an old question, but I recently got the same issue. And I haven't seen an answer that works for me, so I'll share the solution I found.
Why doesn't or can't the int array be coerced or boxed to an object[] or string[]? Why it isn't boxed, I don't know. But it can be boxed explicitly, see solution below.
Why doesn't the compiler catch this? Because the compiler misinterprets the situation: The type isn't exactly an object array, so it doesn't know what to do with it and decides to perform a .ToString() on the int array, which returns one single parameter containing the type name rather than the parameter list itself. It doesn't do that with a string array, because the target type is already a string - but with any other kind of array the same issue happens (for example bool[]). Consider var arr1 = new int[]{1,2}; with string.Format("{0}", arr1): As long as you have only {0} in the format string, you get only the type name "System.Int32[]" back (and no exception occurs). If you have more placeholders, e.g. string.Format("{0}{1}", arr1), then the exception occurs - because arr1 is misinterpreted as one parameter - and for the compiler, a 2nd one is missing. But what I think is a conceptional bug is that you can't convert arr1, i.e. if you try to do (object[])arr1- you're getting:
CS0030 Cannot convert type 'int[]' to 'object[]'
Solution:
Filling in each element of the int array is not a solution that works for me, because in my project I am creating a format template string dynamically during runtime containing the {0}...{n} - hence I need to pass an array to String.Format.
So I found the following workaround. I created a generic helper function (which of course could be an extension method too if you prefer):
// converts any array to object[] and avoids FormatException
object[] Convert<T>(T[] arr)
{
var obj = new List<object>();
foreach (var item in arr)
{
obj.Add((object)item);
}
return obj.ToArray();
}
Now if you try that in the example below which is showing up the FormatException:
// FormatException: Index (zero based) must be greater than or equal to zero
// and less than the size of the argument list
var arr1 = (new int[] { 1, 2 });
string.Format("{0}{1}{0}{1}", arr1).Dump();
Fix: Use Convert(arr1) as 2nd parameter for string.Format(...) as shown below:
// Workaround: This shows 1212, as expected
var arr1 = (new int[] { 1, 2 });
string.Format("{0}{1}{0}{1}", Convert(arr1)).Dump();
Try example as DotNetFiddle
Conclusion:
As it seems, the .NET runtime really misinterprets the parameter by applying a .ToString() to it, if it is not already of type object[]. The Convert method gives the runtime no other choice than to do it the right way, because it returns the expected type. I found that an explicit type conversion did not work, hence the helper function was needed.
Note: If you invoke the method many times in a loop and you're concerned about speed, you could also convert everything to a string array which is probably most efficient:
// converts any array to string[] and avoids FormatException
string[] ConvertStr<T>(T[] arr)
{
var strArr = new string[arr.Length];
for (int i = 0; i < arr.Length; i++)
{
strArr[i]=arr[i].ToString();
}
return strArr;
}
This is working as well. To convert from a different datatype, such as a dictionary, you can simply use
string[] Convert<K,V>(Dictionary<K,V> coll)
{
return ConvertStr<V>(coll.Values.ToArray());
}
Update: With string interpolation, another short way to solve it is:
var baz = string.Format("{0} and {1}", myInts.Select(s => $"{s}").ToArray());
Your string.Format is expecting 2 arguments ({0} and {1}). You are only supplying 1 argument (the int[]). You need something more like this:
string bar = string.Format("{0} and {1}", myInts[0], myInts[1]);
The compiler does not notice the problem because the format string is evaluated at runtime. IE The compiler doesn't know that {0} and {1} mean there should be 2 arguments.
This works:
string bar = string.Format("{0} and {1}", myInts[0], myInts[1]);
The compiler doesn't catch it because it doesn't evaluate your format string.
The example you gave up top doesn't match what you're trying to do down below... you provided two {} and two arguments, but in the bottom one you only provided one argument.

Categories