来自C ++背景,我遇到了基于泛型类型的特定实例的重载问题。以下不起作用,因为只生成Foo<T>类的代码实例,因此在Method中,this的类型只是Foo<A>,而不是Foo<B>或<=>正如我所希望的那样。在C ++中,我习惯将模板实例化为唯一类型。

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中的代码复制为我想要用于的每种类型的单独类。

编辑: 希望这更清楚一点。

有帮助吗?

解决方案

这适用于静态情况。处理实例函数会有点复杂。这个来自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;
}

其他提示

我现在有一段真正完整的代码来证明这个问题。 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);
    }
}

是的,这不会编译。你到底想要做什么呢?请记住,重载决策是在编译时执行的,而不是执行时。正如falling888所说,你可以转换并调用适当的重载方法 - 但是你希望编译器选择哪两个重载?您希望它与Foo<string>而不是Foo<A>Foo<B>

有什么关系

这一切都证明.NET泛型确实与C ++模板有很大不同,当然......

我没有尝试过,但似乎你应该能够通过制作A <!>放大器来达到你想要的效果; B可访问(例如,使用非循环访问者模式)。

编辑:我不确定您是否可以在尝试时完成此操作。我已经尝试了各种各样的技巧来尝试使其工作,并且无法将其编译。我能做的最好的事情是将方法调用拉到我的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,一个用于字符串)和两个引用常规类型(一个用于int,一个用于字符串),这一切都正常...这就是你的意思'之后?

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

我希望找到一种更简单的方法来做到这一点但是现在我要用这个:

用这些类替换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());

这至少不需要在<=>中公布代码,只需要我转发构造函数。

许可以下: CC-BY-SA归因
不隶属于 StackOverflow
scroll top