struct ToString() not automatically invoked

Z

Zytan

Everything (er, every class) in C# has ToString() which is
conveniently automatically invoked when using it in Debug.WriteLine()
or in a string concatenation, etc. I made a struct, and I want to
make a method to print out its data in a similar format. So, I did
this:

public struct MyStruct
{
public override string ToString()
{
return ("Hello");
}
}

But, it doesn't get automatically invoked. It complains if I don't
put "public" or "override", so I assume it knows that it's overriding
something (I am not sure what, since it's not a class, and thus not an
object). So:
1. why does it complain without "override"? and
2. why isn't it automatically invoked?

I know classes are references and structs are value types. Since the
struct isn't a class, I don't even know *why* it is complaining about
my creation of a ToString() method in it. I would expect it wouldn't
complain, and also wouldn't be invoked automatically. But, since it
does complain surprisingly, I thought maybe it would also be invoked
automatically, which it doesn't.

Zytan
 
M

Mattias Sjögren

1. why does it complain without "override"? and

Because a method with the same signature is defined in a base class
(System.Object in this case).

2. why isn't it automatically invoked?

In what situation is it not invoked? The following prints "Hello
Hello" as expected for me

public struct MyStruct
{
public override string ToString()
{
return ("Hello");
}
}

class Test
{
static void Main()
{
MyStruct m = new MyStruct();
Console.WriteLine("Hello " + m);
}
}


Mattias
 
J

Jon Skeet [C# MVP]

Zytan said:
Everything (er, every class) in C# has ToString() which is
conveniently automatically invoked when using it in Debug.WriteLine()
or in a string concatenation, etc. I made a struct, and I want to
make a method to print out its data in a similar format. So, I did
this:

public struct MyStruct
{
public override string ToString()
{
return ("Hello");
}
}

But, it doesn't get automatically invoked.

In what situation? Here's a test which shows it being invoked:

using System;

public struct MyStruct
{
public override string ToString()
{
return ("Hello");
}
}

class Test
{
static void Main()
{
MyStruct foo = new MyStruct();
Console.WriteLine (foo);
}
}
It complains if I don't
put "public" or "override", so I assume it knows that it's overriding
something (I am not sure what, since it's not a class, and thus not an
object).

It still (sort of) derives from object - you are genuinely overriding a
method here. The details are pretty fiddly.
So:
1. why does it complain without "override"? and

Because then you'd be hiding the method instead of overriding it.
2. why isn't it automatically invoked?

When are you expecting it to be automatically invoked?
I know classes are references and structs are value types. Since the
struct isn't a class, I don't even know *why* it is complaining about
my creation of a ToString() method in it. I would expect it wouldn't
complain, and also wouldn't be invoked automatically. But, since it
does complain surprisingly, I thought maybe it would also be invoked
automatically, which it doesn't.

Without more details of when you'd expect it to be invoked, it's
impossible to answer you.

Could you post a short but complete program which demonstrates the
problem?

See http://www.pobox.com/~skeet/csharp/complete.html for details of
what I mean by that.
 
B

Bill Butler

I don't know what you are doing wrong. It should work.

---------------------------------
using System;

class Test
{
public static void Main()
{
MyStruct x = new MyStruct();
Console.WriteLine("{0}", x);
}
}

public struct MyStruct
{
public override string ToString()
{
return ("Hello");
}
}
 
Z

Zytan

Ok, you guys rock, thanks for posting the code samples. Jon, I've
created a 'complete' program that shows the error (and yes, it
certainly helps to narrow down exactly what is wrong, and most often
shows the err in your ways). The problem occurs when I have my *own
function* that accepts a string. In this case, the conversion from
struct to string does not implicitly take place.

class Program
{
public struct MyStruct
{
public int x;
public override string ToString()
{
return "Hello!";
}
}
static void Main(string[] args)
{
MyStruct s;
s.x = 5;
Print(s); // doesn't compile
Print(s.ToString()); // compiles
}
static void Print(string s)
{
Console.WriteLine(s);
}
}

Zytan
 
Z

Zytan

1. why does it complain without "override"? and
Because a method with the same signature is defined in a base class
(System.Object in this case).

Ok, so, structs DO derive from object. Strange. I guess the evidence
suggest that, doesn't it?
In what situation is it not invoked?

Please see my program in another post in this thread.

Thanks, Mattias

Zytan
 
Z

Zytan

But, it doesn't get automatically invoked.
In what situation?

Please see my program in another post.
It still (sort of) derives from object - you are genuinely overriding a
method here. The details are pretty fiddly.
Ah.


Because then you'd be hiding the method instead of overriding it.

Yes, of course. Now that I know it's derived from object, it makes
sense.
When are you expecting it to be automatically invoked?

It's when I pass it to my own function that accepts a string
parameter.
Please see the program in another post.

Thanks, Jon

Zytan
 
Z

Zytan

I don't know what you are doing wrong. It should work.

I don't think I am doing anything wrong. Call your own function that
takes a string instead of Console.WriteLine in your program, and
you'll see that it doesn't work.

Please see my program in another post.

Thanks, Bill.

Zytan
 
J

Jon Skeet [C# MVP]

Zytan said:
Ok, you guys rock, thanks for posting the code samples. Jon, I've
created a 'complete' program that shows the error (and yes, it
certainly helps to narrow down exactly what is wrong, and most often
shows the err in your ways). The problem occurs when I have my *own
function* that accepts a string. In this case, the conversion from
struct to string does not implicitly take place.

No, it doesn't - and it doesn't for classes either, thankfully. Why did
you expect that it would?

You could add your own implicit conversion to a string, although I
wouldn't recommend it.
 
B

Bill Butler

Zytan said:
I don't think I am doing anything wrong. Call your own function that
takes a string instead of Console.WriteLine in your program, and
you'll see that it doesn't work.

You could overload your Print method a few times to get the behavior
that you desire

for instance, this would work.

static void Print(object obj)
{
Console.WriteLine(obj);
}


You could make your Print method accept formatting strings as well if
you write those overloads

Good luck
Bill
 
Z

Zytan

No, it doesn't - and it doesn't for classes either, thankfully.

Why thankfully?
Why did you expect that it would?

It does for Console.WriteLine which takes a string.
Why not for MyOwnFunction which takes a string?
I see no difference between the two.

(I personally was surprised that C# does this at all implicitly, since
it does so much to ensure proper / strict type checking, so I don't
care that I must do astruct.ToString() manually.)
You could add your own implicit conversion to a string, although I
wouldn't recommend it.

Ok, I don't think you mean using Print(astruct.ToString()), so you
must mean adding such capability to the struct itself, such that I can
do Print(astruct). I'd prefer not to do that anyway. But, why is it
needed? And why is it not recommended?

Zytan
 
Z

Zytan

I don't think I am doing anything wrong. Call your own function that
You could overload your Print method a few times to get the behavior
that you desire

for instance, this would work.

static void Print(object obj)
{
Console.WriteLine(obj);
}

Yes, you're right, that works!

But, why does this work, and using 'string' as the parameter type
doesn't? Doesn't Console.WriteLine also accept 'string', this being
no different than my original Print method that takes a string? What
gives?
You could make your Print method accept formatting strings as well if
you write those overloads

Please elaborate, Bill, if you don't mind. I am unsure what you mean
by accepting 'formatting strings'? You mean like: Print(string
format, params object[] args), in the way Console.WriteLine does?
Good luck

Thanks!

Zytan
 
K

Kevin Frey

Console.WriteLine can take a string, and it is also overloaded numerous ways
to take Boolean, Decimal, int, etc. One of the overloads takes an Object.

You have *assumed* that when you do Console.WriteLine( yourobject ) that you
have in fact invoked the version that takes the String argument.

I severely doubt this is the case. I think you are invoking the version that
takes an object, and *that* version of Console.WriteLine does the
ToString( ) for you.

So you are presuming there is an implicit *conversion* going on here when in
fact I don't think there is.

If you want your function to take your object without requiring a
ToString( ) conversion, do exactly what Console.WriteLine does and have it
accept an Object and then have the function perform the ToString( ). Note
that a ToString( ) still occurs, it's just that you don't have to hand-code
it everywhere.

But be warned that implicit conversions (or mechanisms that look like they
behave like implicit conversions) can make code more mysterious. When used
in appropriates cases (and Console.WriteLine is an appropriate case because
it is arguably generic behaviour), it makes for an effective interface.

When used for the sake of reducing your typing, then you have chosen this
requirement over and above good design.

Kevin
 
B

Bill Butler

<snip
Yes, you're right, that works!

But, why does this work, and using 'string' as the parameter type
doesn't? Doesn't Console.WriteLine also accept 'string', this being
no different than my original Print method that takes a string? What
gives?

No, it is using object as the parameter type in this case.
Console.WriteLine calls ToString() on any object passed to it.
Console.WriteLine has about 20 overloads {So do the other classes that
expose WriteLine}
see:
http://msdn2.microsoft.com/en-us/library/system.console.writeline.aspx

Samples:
Console.WriteLine("Hello"); // WriteLine(string);
Console.WriteLine(333); // WriteLine(int);
X x = new X();
Console.WriteLine(x); // WriteLine(object);
Console.WriteLine("[{0}]",33"); // WriteLine(string, object);
Console.WriteLine("{0}^2={1}", 5, 5*5); // WriteLine(string, object,
object);
etc

You could make your Print method accept formatting strings as well if
you write those overloads

Please elaborate, Bill, if you don't mind. I am unsure what you mean
by accepting 'formatting strings'? You mean like: Print(string
format, params object[] args), in the way Console.WriteLine does?

Yes, You simply provide your own overloads to mimic those used in
Console.WriteLine
For instance, Suppose you wanted to slap a timestamp on everything that
you send to YOUR Print method.
You create a few overloads for Print and voila

Here..take a look.


using System;

class Program
{
public struct MyStruct
{
public int x;
public override string ToString()
{
return "Hello!";
}
}
static void Main(string[] args)
{

MyStruct s;
s.x = 5;
Print("qwerty");
Print(s);
Print("[{0}]",33);
Print("{0}^2={1}", 5, 5*5);
}

static void Print(object obj)
{
Print(obj.ToString());
}

public static void Print(string format, object obj1)
{
string data =string.Format(format, obj1);
Print(data);
}

public static void Print(string format, object obj1, object obj2)
{
string data =string.Format(format, obj1, obj2);
Print(data);
}

public static void Print(string format, object obj1, object obj2,
object obj3)
{
string data =string.Format(format, obj1, obj2, obj3);
Print(data);
}

public static void Print(string format, params object[] objs)
{
string data =string.Format(format, objs);
Print(data);
}

public static void Print(string data)
{
Console.WriteLine("{0:HH:mm:ss.ff} {1}", DateTime.Now, data );
}

}
 
Z

Zytan

But, why does this work, and using 'string' as the parameter type
No, it is using object as the parameter type in this case.
Console.WriteLine calls ToString() on any object passed to it.
Console.WriteLine has about 20 overloads {So do the other classes that
expose WriteLine}
see:http://msdn2.microsoft.com/en-us/library/system.console.writeline.aspx

Yes, I knew it had (single parameter) overloads (although I was never
sure why, since ToString() is invoked on anything passed in). But,
the one I was calling was Console.WriteLine(string), not
WriteLine(object). But yes, MSDN shows:
http://msdn2.microsoft.com/en-us/library/swx4tc5e.aspx
"If value is a null reference (Nothing in Visual Basic), only the line
terminator is written. Otherwise, the ToString method of value is
called to produce its string representation, and the resulting string
is written to the standard output stream."

So, now it makes sense. Since ToString() is available for everything
(including structs, as I've found), WriteLine(object) works for
anything. Add, the compiler doesn't just blindly convert anything
into a string as it wishes, it was merely calling WriteLine(object).

(Although, when you concatenate strings, it does invoke ToString()
automatically, so it seems to be implicitly converting anything into a
string as it wishes, so that's strange.)

(And, this begs the question: Why are the other [single parameter]
WriteLine overloads even needed, if you have WriteLine(object)?)
Yes, You simply provide your own overloads to mimic those used in
Console.WriteLine
For instance, Suppose you wanted to slap a timestamp on everything that
you send to YOUR Print method.
You create a few overloads for Print and voila

Right, I could mimic Console.WriteLine. Well, my function is for
logging, so perhaps I should just use the Print(object) style, and let
it call the ToString() on anything I pass in. Isn't that good enough
to cover basic Console.WriteLine (the single parameter overloads)
functionality?

Zytan
 
Z

Zytan

You have *assumed* that when you do Console.WriteLine( yourobject ) that you
have in fact invoked the version that takes the String argument.

Indeed I have.
I severely doubt this is the case. I think you are invoking the version that
takes an object, and *that* version of Console.WriteLine does the
ToString( ) for you.

Yup. That's exactly what's happening.
So you are presuming there is an implicit *conversion* going on here when in
fact I don't think there is.

Yes! And I think this arose from the fact that concantenating strings
implicitly invokes ToString(), and I may even have read somewhere (or
maybe just thought it myself) that the same thing happens when using
Debug.WriteLine or Console.WriteLine.
If you want your function to take your object without requiring a
ToString( ) conversion, do exactly what Console.WriteLine does and have it
accept an Object and then have the function perform the ToString( ). Note
that a ToString( ) still occurs, it's just that you don't have to hand-code
it everywhere.

Yup! This is the solution. Thanks.
But be warned that implicit conversions (or mechanisms that look like they
behave like implicit conversions) can make code more mysterious. When used
in appropriates cases (and Console.WriteLine is an appropriate case because
it is arguably generic behaviour), it makes for an effective interface.

I totally agree. My function is a logfile WriteLine-style of
function, so this should be one of those cases. I wouldn't do this
elsewhere, and I actually disliked the thought that C#, with all of
its strict type checking and explicit type conversion requirements
(like bool and int), was actually doing something as horrid as
implicitly converting anything I give it to a string! Now I feel
better. And I wouldn't implement this normally, except, as you say,
for WriteLine-style behaviour, it's accepted, and ok.
When used for the sake of reducing your typing, then you have chosen this
requirement over and above good design.

Yes. I prefer to be verbose and proper, than quick and dirty.

Thanks, Kevin

Zytan
 
B

Bill Butler

(And, this begs the question: Why are the other [single parameter]
WriteLine overloads even needed, if you have WriteLine(object)?)

Efficiency. That is the same reason it has
Writeline(string format, obj1);
Writeline(string format, obj1, obj2);
Writeline(string format, obj1, obj2, obj3);

when
Writeline(string format, params object[] objs);
would have sufficed. These overrides perform better.
Right, I could mimic Console.WriteLine. Well, my function is for
logging, so perhaps I should just use the Print(object) style, and let
it call the ToString() on anything I pass in. Isn't that good enough
to cover basic Console.WriteLine (the single parameter overloads)
functionality?

It would work. You have to decide if it is "Good enough".

Good Luck
Bill
 
J

Jon Skeet [C# MVP]

Zytan said:
Why thankfully?

Because implicit conversions are generally a bad idea.
It does for Console.WriteLine which takes a string.
Why not for MyOwnFunction which takes a string?
I see no difference between the two.

Console.WriteLine is overloaded, and there's one overload which takes
an object.
(I personally was surprised that C# does this at all implicitly, since
it does so much to ensure proper / strict type checking, so I don't
care that I must do astruct.ToString() manually.)

Exactly - strictness is generally a good idea.
Ok, I don't think you mean using Print(astruct.ToString()), so you
must mean adding such capability to the struct itself, such that I can
do Print(astruct). I'd prefer not to do that anyway. But, why is it
needed? And why is it not recommended?

I don't recommend it because I don't like conversions going on which
aren't obvious when reading the code. I think I've covered why it's
"needed" already.
 
J

Jon Skeet [C# MVP]

Bill Butler said:
(And, this begs the question: Why are the other [single parameter]
WriteLine overloads even needed, if you have WriteLine(object)?)

Efficiency. That is the same reason it has
Writeline(string format, obj1);
Writeline(string format, obj1, obj2);
Writeline(string format, obj1, obj2, obj3);

when
Writeline(string format, params object[] objs);
would have sufficed. These overrides perform better.

No, that's not for efficiency - it's to help those languages which
don't support "params" parameters automatically. Internally the
overloads which don't take an array create an array anyway.
 
J

Jon Skeet [C# MVP]

(Although, when you concatenate strings, it does invoke ToString()
automatically, so it seems to be implicitly converting anything into a
string as it wishes, so that's strange.)

String concatenation is an exception (which is part of the C# language,
by the way, not something done automatically by .NET) which is just for
sake of convenience. Sometimes purity comes second to practicality :)
 

Ask a Question

Want to reply to this thread or ask your own question?

You'll need to choose a username for the site, which only take a couple of moments. After that, you can post your question and our members will help you out.

Ask a Question

Top