1

来自 C++ 背景,我遇到了基于泛型类型的特定实例的重载问题。以下内容不起作用,因为只Foo<T>生成了一次该类的代码实例,所以在里面Method,类型this是简单的,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 所说,您可以强制转换并调用适当的重载方法 - 但是您希望编译器选择两个重载中的哪一个?你想让它做什么Foo<string>而不是Foo<A>or 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

编辑:我不确定您是否可以在尝试时完成此操作。我已经尝试了各种技巧来尝试让它工作并且无法让它编译。我能做的最好的就是将方法调用拉到我的 Generic 类之外。如果您的方法调用在外部,那么您可以专门定义泛型中的 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

我有四个重载函数引用两个引用泛型类(一个用于 int,一个用于 string)和两个引用常规类型(一个用于 int,一个用于 string),一切正常......这就是你想要的?

编辑:问题似乎与重载方法的调用无关,它与您的 foreach 有关,它试图将列表中的所有项目转换为与第一个相同的类型以引用重载方法。不符合该确切定义的第一项将导致您的编译失败。

于 2009-01-01T20:36:57.367 回答
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 回答