14

ToString のオーバーロードがゼロの引数を取ることができるのはどうしてですか? 引数なしの ToString は System.Object の一部です!

クローズ投票に応じて編集: ビルド サーバーを .NET 4.5 にアップグレードする立場にないので、このコードを .NET 4.0/VS 2010 コンパイラで動作させる方法はありますか? オーバーロードにまったく別の名前を付けることはできませんが、これはクールではありません。

  • 問題のオブジェクトは、System.Object から継承された ToString メソッドをオーバーライドする F# Discriminated Union です。
  • オーバーライドされた ToString は、同じソリューションの一部である C# プロジェクトのコードによって呼び出されています。
  • ToString の余分なオーバーロードを識別された共用体に追加するまで、これはすべて正常に機能しました。これは 1 つの引数を取ります。
  • すべてが私のローカル マシン上で完全にビルドおよび実行されます (VS 2012、.NET 4.0 を対象とするすべてのプロジェクト)
  • ビルド サーバー (.NET 4.0) で失敗します。識別された共用体に ToString の 1 引数のオーバーロードを追加するとすぐに、タイトルの C# コンパイラ エラーが表示されました。

最も簡単な回避策は、ToString オーバーロードの名前を別の名前に変更することだと思いますが、これは奇妙です。

以下は簡単な再現です。このコードを含むソリューションの zip ファイルは、http: //dl.dropbox.com/u/1742470/CS1501_Repro.zipからダウンロードできます。

VS 2010 でそのソリューションをコンパイルすると、「CS1501: メソッド 'ToString' のオーバーロードが 0 引数を取らない」で失敗します。VS 2012 でコンパイルすると問題なく動作します。どちらの場合も、.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

これは部分的な回答にすぎませんが、VS2010 と VS2012 が F# クラスに対して異なる IL を生成することに気付きました。メソッドの IL をToString(lookup:Func<int,string>)見ると、VS2010 の出力が次のようになっていることがわかります。

.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

VS2012 の出力は次のとおりです。

.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

違いはhidebysig、メソッド シグネチャの注釈です。が追加された場合hidebysig、メソッドは同じ名前と署名を共有する他の実装 (基本クラス内) をhidebysig非表示にしますが、 が省略された場合は、署名が異なっていても同じ名前の他のすべての実装を非表示にします。このスタック オーバーフローに関する質問には、 を説明する適切な回答が含まれていますhidebysig

したがって、この回答は問題を解決しませんが、VS2010 でコンパイルされない理由を説明しています。

于 2012-08-21T23:18:08.347 に答える
5

私が F# を使用したのはこれが初めてなので、このソリューションはおそらく最もエレガントではないことを認めなければなりませんが、ToString() は System.Object の一部であると述べました...

この問題を解決するには、_item をオブジェクトに割り当てる DoThing() メソッドにコード行を追加し、オブジェクトから ToString() を呼び出します。この例では、目的の結果が得られます。

F# コード (変更なし)

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# コード (DoThing() メソッドの変更を参照)

    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());
        }
    }

これがあなたの望んでいるものではない場合は申し訳ありません...しかし、それは私が考えることができるすべてです.誰かがそれを行うのが間違っていると思うなら、理由を教えてください!

于 2012-08-21T23:08:37.173 に答える