1

C++ のバックグラウンドを持っているので、ジェネリック型の特定のインスタンスに基づくオーバーロードで問題が発生しました。Foo<T>クラスのコードのインスタンスが 1 回しか生成されないため、次のコードは機能MethodthisませFoo<T>ん。C++ では、テンプレートが一意の型としてインスタンス化されることに慣れています。Foo<A>Foo<B>

using System.Collections.Generic;
class A
{
    // Concrete class
}

class B
{
    // Concrete class
}

class Bar
{
    public void OverloadedMethod(Foo<A> a) {} // do some A related stuff
    public void OverloadedMethod(Foo<B> b) {} // do some B related stuff
    public void OverloadedMethod(OtherFoo of) {} // do some other stuff

     public void VisitFoo(FooBase fb) { fb.Method(this); }
}

abstract class FooBase
{
    public abstract void Method(Bar b);
}

class Foo<T> : FooBase
{
    // Class that deals with As and Bs in an identical fashion.
    public override void Method(Bar b)
    {
        // Doesn't compile here
        b.OverloadedMethod(this);
    }
}

class OtherFoo : FooBase
{
    public override void Method(Bar b)
    {
        b.OverloadedMethod(this);
    }
}

class Program
{
    static void Main(string[] args)
    {
        List<FooBase> ListOfFoos = new List<FooBase>();
        ListOfFoos.Add(new OtherFoo());
        ListOfFoos.Add(new Foo<A>());
        ListOfFoos.Add(new Foo<B>());

        Bar b = new Bar();
        foreach (FooBase fb in ListOfFoos)
            b.VisitFoo(fb);
        // Hopefully call each of the Bar::Overloaded methods
    }
}

このようなものを C# で動作させる方法はありますか? Foo のコードを、使用したい型ごとに個別のクラスとして複製する必要はありません。

編集:うまくいけば、これはもう少し明確です。

4

5 に答える 5

3

私は今、問題を示す真に完全なコードを手に入れました。OPへの注意:投稿する前にコードをコンパイルしてみてください。ここまでやるには、やらなければならないことがたくさんありました。他の人があなたを助けるのをできるだけ簡単にするのは良いことです. また、余分なビットの束を削除しました。ここでは、OtherFoo はあまり関係がなく、FooBase も関係ありません。

class A {}
class B {}

class Bar
{
    public static void OverloadedMethod(Foo<A> a) { }
    public static void OverloadedMethod(Foo<B> b) { }
}

class Foo<T>
{
    // Class that deals with As and Bs in an identical fashion.
    public void Method()
    {
        // Doesn't compile here
        Bar.OverloadedMethod(this);
    }
}

はい、これはコンパイルされません。正確に何をすることを期待していましたか?オーバーロードの解決は、実行時ではなくコンパイル時に実行されることに注意してください。fall888 が言うように、オーバーロードされた適切なメソッドをキャストして呼び出すことはできますが、コンパイラがそれ以外の場合に選択するのは、2 つのオーバーロードのうちどれですか? Foo<string>またはの代わりに何をしたいですFoo<A>Foo<B>

もちろん、これはすべて、.NETジェネリックが実際にC++テンプレートと大きく異なることを示しています...

于 2009-01-01T20:47:23.680 に答える
2

私は試していませんが、A&Bを訪問可能にすることで(たとえば、非周期的な訪問者パターンを使用して)、希望することを達成できるはずです。

于 2009-01-01T21:06:41.537 に答える
1

これは、静的なケースで機能します。インスタンス関数を扱うのはもう少し複雑です。Jon Skeet からのこの投稿は、インスタンス メソッドを処理する合理的な方法を提供する可能性があります。

class Program
{
    static void Main(string[] args)
    {
        var testA = new Foo<A>();
        testA.Method();
        var testB = new Foo<B>();
        testB.Method();
        Console.ReadLine();
        var testString = new Foo<string>(); //Fails
        testString.Method(); 
        Console.ReadLine();
    }
}

class A { }
class B { }
class Bar
{
    public static void OverloadedMethod(Foo<A> a)
    {
        Console.WriteLine("A");
    }
    public static void OverloadedMethod(Foo<B> b)
    {
        Console.WriteLine("B");
    }
}
class Foo<T>
{
    static Foo()
    {
        overloaded = (Action<Foo<T>>)Delegate.CreateDelegate(typeof(Action<Foo<T>>), typeof(Bar).GetMethod("OverloadedMethod", new Type[] { typeof(Foo<T>) }));
    }

    public void Method()
    {
        overloaded(this);
    }

    private static readonly Action<Foo<T>> overloaded;
}
于 2009-01-02T12:34:37.080 に答える
0

私はこれを行うためのより簡単な方法を見つけることを望んでいましたが、今のところ私はこれで行きます:

Foo<T>クラスを次のクラスに置き換えます。

abstract class Foo<T> : FooBase
{
    // Class that deals with As and Bs in an identical fashion.
}

class Foo_A : Foo<A>
{
    public override void Method(Bar b)
    {
        b.OverloadedMethod(this);
    }
}

class Foo_B : Foo<B>
{
    public override void Method(Bar b)
    {
        // Doesn't compile here
        b.OverloadedMethod(this);
    }
}

そして、インスタンス化をに変更します

List<FooBase> ListOfFoos = new List<FooBase>();
ListOfFoos.Add(new OtherFoo());
ListOfFoos.Add(new Foo_A());
ListOfFoos.Add(new Foo_B());

これには、少なくともでコードを複製する必要はなくFoo<T>、コンストラクターを転送する必要があります。

于 2009-01-01T21:22:19.383 に答える
0

編集:あなたが試みているように、これを完了することができるかどうかはわかりません. これを機能させるためにあらゆる種類のトリックを試しましたが、コンパイルできません。私にできる最善の方法は、ジェネリック クラスの外でメソッド呼び出しをプルすることです。メソッド呼び出しが外部にある場合は、T がジェネリックにあるものを具体的に定義できます。ただし、メソッド内では、コンパイル時に、コンパイラは T が何であるかを知らないため、どのオーバーロードされたメソッドを呼び出すかを知りません。これを回避できる唯一の方法は、スイッチを使用して T の型を判別し、呼び出すオーバーロードを手動で指定することです。

私ができる最善の方法はこれです。これはあなたが求めているものとはまったく異なりますが、同様の効果に使用できます。

class Stuff<T>
{
    public T value { get; set; }
}

class Program
{
    static void DummyFunc(Stuff<int> inst)
    {
        Console.WriteLine("Stuff<int>: {0}", inst.value.ToString());
    }

    static void DummyFunc(Stuff<string> inst)
    {
        Console.WriteLine("Stuff<string>: {0}", inst.value);
    }
    static void DummyFunc(int value)
    {
        Console.WriteLine("int: {0}", value.ToString());
    }
    static void DummyFunc(string value)
    {
        Console.WriteLine("string: {0}", value);
    }
    static void Main(string[] args)
    {
        var a = new Stuff<string>();
        a.value = "HelloWorld";

        var b = new Stuff<int>();
        b.value = 1;

        var c = "HelloWorld";
        var d = 1;

        DummyFunc(a);
        DummyFunc(b);
        DummyFunc(c);
        DummyFunc(d);
    }
}

そして出力を得ました:

Stuff<string>: HelloWorld
Stuff<int>: 1
string: HelloWorld
int: 1

2 つの参照ジェネリック クラス (1 つは int 用、もう 1 つは文字列用) と 2 つの参照通常型 (1 つは int 用、もう 1 つは文字列用) を参照する 4 つのオーバーロードされた関数がありますが、すべて正常に動作します...これはあなたが求めているものですか? ?

編集:オーバーロードされたメソッドの呼び出しに問題があるようには見えません。オーバーロードされたメソッドを参照するために、リスト内のすべての項目を最初のものと同じ型に変換しようとしている foreach に関係しています。その正確な定義に準拠していない最初の項目は、コンパイルが失敗する原因になります。

于 2009-01-01T20:36:57.367 に答える