Closed ghost closed 4 years ago
Since Console.WriteLine()
just calls ToString()
on the passed-in value, I'm going to assume the change you're proposing is to alter the output of ToString()
for arrays (and possibly also for other collection types).
I would very much like to see this change, but I'm worried it has problems with backwards compatibility. Apart from the obvious case where some code could be depending on the exact output of ToString
, this change could make existing code stop working in some edge cases. Specifically:
ToString()
on some value in the array throws an exception, does it mean calling ToString()
on the array now starts throwing?OutOfMemoryException
, since ToString()
previously produced ~30 B string, but could start producing strings that are hundreds of megabytes large, or even larger.What is the use case for this though? How often do you need to print the output of arrays? It certainly shouldn't just by default print the contents....that breaks backwards compatibility and has performance and potentially even security implications if it suddenly changes (logs, etc).
Why not a write a 1 line extension method to arrays if you really need this often? This way you can control exactly how you want it to be output and formatted, decide what it should do if the array has a large number of entries, where and which arrays should be output like that, how NULL should be formatted, etc, etc.
On a side note, I find going around telling people you disagree with to "get out of here" and calling them arrogant generally not very helpful when trying to convince others.
@cup -
Part of the issue is that it's almost always recommended - even in the other languages - to not use simple print
(and similar equivalent) statements to output arrays; instead, you're supposed to explicitly format the output, so you have better control over it.
print
then tends to get relegated to debug messaging. At which point the answers in that SO Q/A you posted seem to be well within the bounds of acceptable code.
@chrisaut I am not trying to convince anyone. I am reporting what I consider to be a significant omission from the runtime. If decision makers decide to do nothing, so be it.
Of course you are, you have to convince the decision makers, it helps to convince them if the broader community agrees.
And if someone is called arrogant, maybe thats because they were being arrogant. I didnt see you disagree with that point, only my response to it.
I really don't wanna delve into that, but nowhere did I see anyone acting arrogant in the like 3 threads across repos you seem to have opened about this by now. So please don't interpret that as me agreeing with you. You are the one that seems to dismiss questions and concern others raise, instead calling them "arrogant" and telling them to be quiet.
Let's discuss the merits of this change, not this kindergarden stuff.
If you want a one-line code, you may try the following:
Console.WriteLine(string.Join(", ", a1));
the string.Join
method will do the looping for you and add the separator between your array elements.
Or you can use an extension method:
using System;
public class Program
{
public static void Main()
{
int[] a1 = {10, 20};
Console.WriteLine(a1.Repr());
}
}
public static class Extension
{
public static string Repr<T>(this T[] array)
{
return string.Join(", ", array);
}
}
You write the Repr just once, and have the Extension class in a separate Extension.cs
file in your project, and you can use it just like I did in Main
.
As others have pointed out, you can currently achieve this as a 1-liner using string.Join
. This creates a new string containing each element of the array separated by the specified string. You can additionally use LINQ to trivially add per element formatting support, etc.
The downside to this approach is that it can be allocation heavy and a simple extension method that iterates over the elements and outputs the individual strings directly may perform better.
There are a lot of APIs where this could apply. This include System.Console
and the various stream/serialization/logging APIs.
@chrisaut
How often do you need to print the output of arrays?
In production code? Almost never. When learning C#, trying something new or debugging without a debugger? I'd say fairly often.
And especially in the "learning C#" case, it would be great if the code to do that was very simple: if you're learning about arrays for the first time, I don't think it should be necessary to also learn about string.Join
at the same time.
@chrisaut
How often do you need to print the output of arrays?
In production code? Almost never. When learning C#, trying something new or debugging without a debugger? I'd say fairly often.
And especially in the "learning C#" case, it would be great if the code to do that was very simple: if you're learning about arrays for the first time, I don't think it should be necessary to also learn about
string.Join
at the same time.
Then, what about adding a new API in the Console class, let's call it WriteCollection
for example. and keep Console.WriteLine as it is to not break existing code. but I'm not sure though if this will have any downsides.
I will add further that the Join
suggestion being proposed is obviously weak.
What is the elements are strings, and what if the elements contain your chosen
delimiter? Again, other languages have multiple ways to handle this gracefully.
Take Go:
package main
import "fmt"
func main() {
a1 := []string{",", "."}
fmt.Println(a1)
fmt.Printf("%q\n", a1)
fmt.Printf("%#v\n", a1)
}
Result:
[, .]
["," "."]
[]string{",", "."}
What is the elements are strings, and what if the elements contain your chosen delimiter?
string.Join
has multiple overloads that let you do the appropriate thing. You can likewise have further customization with LINQ.
For example, the following logic is a "one-line" equivalent for each of the above. There are multiple other ways you could do the same using string.Join
.
using System;
using System.Linq;
class Program
{
static void Main()
{
var a1 = new string[] { ",", "." };
Console.WriteLine(string.Join(' ', a1));
Console.WriteLine(string.Join(' ', a1.Select((x) => $"\"{x}\"")));
Console.WriteLine($"{a1}{{{string.Join(", ", a1.Select((x) => $"\"{x}\""))}}}");
// , .
// "," "."
// System.String[]{",", "."}
}
}
The downside, as mentioned above, is just that it pre-allocates the string rather than doing it piece-meal. The latter would use less memory and may be faster overall which may be beneficial in some scenarios. However, it would likely need to be some new API as changing the default formatting of an array or IEnumerable would be breaking (and its not clear that would be breaking in a good way).
@tannergooding again, that is a weak solution. What if one of your elements contains a quote marker ("
)? Again with Go and others, it is simple:
package main
import "fmt"
func main() {
a1 := []string{"\"", "'"}
fmt.Println(a1)
fmt.Printf("%q\n", a1)
fmt.Printf("%#v\n", a1)
}
Result:
[" ']
["\"" "'"]
[]string{"\"", "'"}
@cup, You can customize things easily. I think the discussion now should be whether to implement a new API or not.
What if one of your elements contains a quote marker (")?
It would "just work":
using System;
using System.Linq;
class Program
{
static void Main()
{
var a1 = new string[] { "\"", "." };
Console.WriteLine(string.Join(' ', a1));
Console.WriteLine(string.Join(' ', a1.Select((x) => $"\"{x}\"")));
Console.WriteLine($"{a1}{{{string.Join(", ", a1.Select((x) => $"\"{x}\""))}}}");
// " .
// """ "."
// System.String[]{""", "."}
}
}
.NET strings, when printed to the console or another stream, don't insert escape characters for quotes or other characters. The behavior of printing strings (even if we exposed a convenience overload for printing arrays) would still be the same and would not automatically escape them. That would be a separate formatting API for strings/chars which could be used in conjunction with the new APIs being proposed here.
@tannergooding that doesnt seem quite right, as """
is not a valid string. It would follow that System.String[]{""", "."}
is not valid either. Proper output for the quoted examples should be valid strings.
that doesnt seem quite right, as """ is not a valid string.
That depends entirely on the language and/or API contract. .NET isn't just for C#, it is for a plethora of languages including VB, F#, and others. It is always on the implementor/consumer of the API to define the "contract" under which values are output, parsed, and read. In .NET, strings are output verbatim without surrounding quotes or escape characters by default.
The call to a1.Select
is what added the surrounding quotes and is what would be responsible for escaping any characters that needed escaping (which can differ between XML, JSON, C#, VB, F#, C++, etc).
@tannergooding what you are describing is common among other languages.
When you print a string, the default is to remove any quoting found in the source code. However for debugging or other purpose, one might wish to preserve quoting. A good example would be string "sunday "
. With normal output you wouldnt be able to "see" the final space. This is why quote output might be desired, so that this input:
" "
"\""
produce this output:
" "
"\""
a robust solution (like one offered by many other programming languages) will assume pathological input, which means that input string could contains delimiters (,
or similar) and quoting characters ("
or similar). I would only be interested in a robust solution that accounts for these cases. I hope you can understand and respect that. Cheers.
And that's fair, you would just need to update this proposal to account for that or open a separate proposal suggesting that System.String
implements IFormattable
and supports format specifies for quoting and escaping (or some alternative API for supporting it).
The API review process is detailed here: https://github.com/dotnet/runtime/blob/master/docs/project/api-review-process.md
We generally prefer "formal" API proposals to be laid out like this example: https://github.com/dotnet/corefx/issues/271. This layout helps the API review process and ensures that the proposed surface, rationale, and existing key discussion points are appropriately captured. (Noting that we only really "require" the Proposed API
section and the other sections are just beneficial).
Proper output for the quoted examples should be valid strings.
... why? This implies you're trying to output valid source code, which is probably better handled by constructing an expression tree/code DOM, and outputting the string representation of that instead.
@Clockwork-Muse what I am suggesting has already been implemented by these languages:
and others. So I think the "why" has already been answered. Clearly some groups believe this is a valuable language/runtime feature. If you dont, you are welcome to your opinion, but I dont wish to litigate how "useful" this feature is.
... For at minimum Rust, the strings printed are debug-only strings (arrays don't implement Display
).
Debug strings are subject to change. That they happen to (currently be) valid source code strings is nice, but not guaranteed.
10 languages jump off of a bridge ...
What other languages do isn't relevant to what .NET, C# or the dozens of other languages that target .NET do. You need to demonstrate where it brings value to this ecosystem specifically.
If you want a method to format a String as valid source in some language, you're free to write one. If you want a format specifier provided by the runtime which will allow you to explicitly specify that you want a String to be formatted as valid source for some language, you can propose such a thing, or you can write your own IFormatProvider
implementation for the language of your choice.
@HaloFour did you have anything contructive to add, or did you just want to follow me around threads trolling?
@cup
I'm sorry you feel that anyone who disagrees with your ideas is "trolling" or "arrogant". It makes it exceptionally difficult to actually have a conversation with you, which will not help your case.
And yes, you have to make a case, at least enough of one to get rally others to the cause. It's not sufficient that some other languages have a particular feature. Many of those languages lack C# or .NET features as well. Different languages/runtimes do different things based on the needs of their communities. So it is on you to "litigate", particularly given this is something that is trivial for you to implement yourself and to your taste. Nobody is going to jump on adding a formatter to the runtime that outputs a particular object as the source in C# without justification for doing so.
@HaloFour this is trolling:
10 languages jump off of a bridge ...
if you disagree with the proposal, just say that. You dont need to use sarcasm or flammatory language. You also dont need to say "oh your PR will definitely be rejected har har". State your objection, and it would help if you provided reasons why it would be a bad idea.
Or if you think its possible but should be implemented in a certain way, say that. Really Ive read your comments and I dont recall a single constructive one. You might want to look inward and stop projecting.
@cup
if you disagree with the proposal, just say that.
I have. My biggest disagreement is with this statement:
and the fact that nearly every major programming language has this capability, it would appear that it is justified, and that you are in the minority.
Or if you think its possible but should be implemented in a certain way, say that.
I also have, it should be implemented as your own helper function. You can publish it to NuGet to benefit others.
I don't think it makes any sense to add a specific formatter to the runtime that will output the contents of an array, and it makes less sense to do so in a format specific to any one of the dozens of programming languages that target the runtime. Even starting down that path is a slippery slope of what constructs get interpreted to what source, what if it's an array of List<string>
or some arbitrary POCOs? People, other than yourself, haven't been asking for this, and the interactive environments that target .NET like PowerShell and C# Interactive already have their own mechanism for outputting the contents of an array.
Anyhow, this is just going in circles. We're not going to come to an agreement on this subject, and that's fine.
@HaloFour so stop posting? My point was never "other languages do it, so C# should too!". The point is that, at least 10 other popular languages have implemented this.
I had hoped that would spur a line of thought in your head, something like, "hm, I wonder why they implemented it? Why did they do that? I @HaloFour think this feature is useless, so why would not 1, but at least 10 other languages implement it? Perhaps it does have some usefulness after all? Maybe not for me, but perhaps for other users? If so how would it be useful to them?"
Sadly none of that line of thought happened for you. It seems you are too stuck in your own viewpoint to see that of others.
@cup
On the contrary, I think it's quite useful for scripting or interactive languages where the output of an expression often needs to be output to the terminal. I can see why a number of scripting languages would include this. But a number of languages with interactive capabilities also don't include this and they rely on facilities on the interactive shell to handle the formatting and output. That includes Java JShell, Scala ammonite, C# interactive, F# interactive and PowerShell, to name a few. In some cases, like PowerShell, that gives the user much more power in that they can define how those objects are formatted.
It's also useful for languages that have awful debuggers where developers are more likely to rely on console output as a way to capture program state.
Anyway, I'll stop posting.
@cup Proper output for the quoted examples should be valid strings.
How do you define valid? There are many languages target dotnet. For example, "\""
is valid for C#, but invalid for VB. You can't just output in C# way, not VB way, F# way, or PowerShell way.
As for the array output API, it is easier to create a new one, instead of making a breaking change. I've written a library called ToStringEx, if you're interested.
Here is another example, Java:
import java.util.Arrays;
class app {
public static void main(String[] args) {
int[] a1 = {10, 20};
System.out.println(Arrays.toString(a1));
}
}
@cup
Yes, because Arrays.toString
is equivalent to String.Join
.
Try System.out.println(a1)
, it does not print the contents of the array. It prints something even less comprehensible than .NET prints, the raw Java type and its hash code in hex, e.g.:
[Ljava.lang.String;@271053e1
Here is another example, Java:
import java.util.Arrays; class app { public static void main(String[] args) { int[] a1 = {10, 20}; System.out.println(Arrays.toString(a1)); } }
That's fair, we may raise a proposal to add Array.ToString
or Array.JoinToString
.
Hm, it seems the Java method is not that great. It seems it @HaloFour is right,
only in the fact that like C# String.Join
, Java Arrays.toString
doesnt
account for ambiguous output at all:
import java.util.Arrays;
class app {
public static void main(String[] args) {
String[] a1 = {"sun, mon", "tue, wed"};
System.out.println(Arrays.toString(a1)); // [sun, mon, tue, wed]
}
}
The original creator of this issue deleted their account and is now a "ghost", so I'm going to close this issue. Thanks.
I re-read this and had a crazy idea. What if we think about this as a "debugger on the console"? That is, we already have the DebuggerDisplay attribute, what if we had a way to dump this display out to the console? Details aside for a minute it solves a couple of problems:
I'm thinking this would be a new API or language construct to does something similar to Console.WriteLine but prefers DebuggerDisplay calls over ToString and uses them as a fallback. I haven't fully thought it through as yet but maybe the idea has legs.
I can create a program like this:
but it doesnt give expected output:
I checked online:
https://stackoverflow.com/questions/16265247
and people suggest to loop through the array. Is that really necessary? Many other languages dont require that, like Go:
Nim:
D:
JavaScript:
Python: