14

How is it possible for no overload of ToString to take zero arguments? The zero-argument ToString is part of System.Object!

Edit in response to the close vote: Since I'm not in a position to upgrade my build server to .NET 4.5, is there any way to make this code work with the .NET 4.0/VS 2010 compilers? Short of giving my overload a completely different name, which is just not cool.

  • The object in question is an F# Discriminated Union that overrides the ToString method inherited from System.Object.
  • The overridden ToString is being called by code in a C# project that's part of the same solution.
  • This all worked fine until I added an extra overload of ToString to my discriminated union, which takes one argument.
  • It all builds and runs perfectly on my local machine (VS 2012, all projects targeting .NET 4.0)
  • It fails on the build server (.NET 4.0) - the C# compiler error in the title showed up as soon as I added the 1-argument overload of ToString to my discriminated union.

I guess the simplest workaround would be to rename my ToString overload to something else, but this is bizarre.

Here's a simple reproduction. You can download a zip file of a solution containing this code here: http://dl.dropbox.com/u/1742470/CS1501_Repro.zip

Compiling that solution in VS 2010 will fail with "CS1501: No overload for method 'ToString' takes 0 arguments". Compiling it in VS 2012 will work just fine. In both cases we're targeting .NET Framework 4.

F#

namespace CS1501_Repro.FSharp

open System

[<Serializable>]
type MyDiscriminatedUnion =
    | Foo of int list
    | Bar of string
    | Baz of int
    | Fizz of float
    | Buzz of DateTimeOffset

    override this.ToString() =
        "Zero Arguments"

    member this.ToString(lookup:Func<int,string>) =
        "One Argument"

C#

using System;
using CS1501_Repro.FSharp;

namespace CS1501_Repro.CSharp
{
    public class Caller
    {
        private MyDiscriminatedUnion _item;

        public Caller(MyDiscriminatedUnion item)
        {
            _item = item;
        }

        public string DoThing()
        {
            return _item.ToString();
        }

        public string DoOtherThing()
        {
            return _item.ToString(i => i.ToString());
        }
    }
}
4

2 回答 2

6

This is just a partial answer, but I noticed that VS2010 and VS2012 generate different IL for the F# class. If you look at the IL for the ToString(lookup:Func<int,string>) method, then you will see that the output in VS2010 is:

.method public instance string  ToString(class [mscorlib]System.Func`2<int32,string> lookup) cil managed
{
  // Code size       7 (0x7)
  .maxstack  3
  IL_0000:  nop
  IL_0001:  ldstr      "One Argument"
  IL_0006:  ret
} // end of method MyDiscriminatedUnion::ToString

while the output in VS2012 is:

.method public hidebysig instance string 
        ToString(class [mscorlib]System.Func`2<int32,string> lookup) cil managed
{
  // Code size       7 (0x7)
  .maxstack  8
  IL_0000:  nop
  IL_0001:  ldstr      "One Argument"
  IL_0006:  ret
} // end of method MyDiscriminatedUnion::ToString

The difference is the hidebysig annotation in the method signature. If hidebysig is added, then the method will hide other implementations (in the base class) that share the same name and signature, while if hidebysig is omitted, it will hide all other implementations with the same name even if the signature is different. This stack overflow question contains a good answer that describes hidebysig.

So while this answer doesn't solve your problem, it explains why it doesn't compile in VS2010.

于 2012-08-21T23:18:08.347 回答
5

I have to admit, this is the first time I have ever used F#, so this solution is probably not the most elegant, however, you mentioned that ToString() is part of System.Object...

I solved this issue by adding a line of code into the DoThing() method which assigns _item to object, and then calls ToString() from the object, giving, in this instance, the desired result:

F# code (unchanged)

type MyDiscriminatedUnion =
    | Foo of int list
    | Bar of string
    | Baz of int
    | Fizz of float
    | Buzz of DateTimeOffset

    override this.ToString() = "Zero Arguments"
    member this.ToString(lookup:Func<int,string>) = "One Argument"

C# code (see change in DoThing() method)

    public class Caller
    {
        private Module1.MyDiscriminatedUnion _item;

        public Caller(Module1.MyDiscriminatedUnion item)
        {
            _item = item;
        }

        public string DoThing()
        {
            object result = _item; //assign to object
            return result.ToString(); //call ToString() on object
        }

        public string DoOtherThing()
        {
            return _item.ToString(i => i.ToString());
        }
    }

I do apologize if this is not what you want...but it's all I can think of, and if anyone thinks it's the wrong way to go about it, please do say why!

于 2012-08-21T23:08:37.173 回答