Como posso sobrecarregar um método C # por instâncias específicas de um tipo genérico

StackOverflow https://stackoverflow.com/questions/405429

  •  03-07-2019
  •  | 
  •  

Pergunta

Vindo de fundo um C ++, eu correr em uma protuberância com sobrecarga com base em uma instância específica de um tipo genérico. O seguinte não funciona uma vez que apenas uma vez instância do código para a classe Foo<T> nunca é gerado, então dentro do Method, o tipo de this é simplesmente Foo<T>, não Foo<A> ou Foo<B> como eu esperava. Em C ++ Estou acostumado a modelos a ser instanciado como tipos originais.

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
    }
}

Existe uma maneira de obter algo como isto para trabalhar em C #? Eu prefiro não ter de duplicar o código em Foo como classes separadas para cada tipo eu quero usá-lo.

Edit: Esperemos que este é um pouco mais claro.

Foi útil?

Solução

Este obras para o caso estático. Lidar com funções exemplo seria um pouco mais complicado. Este post de Jon Skeet pode fornecer uma maneira razoável para lidar com os métodos de instância.

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

Outras dicas

Agora tenho um pedaço verdadeiramente completa de código que demonstra o problema. Nota para o OP: por favor, tente compilar o código antes de publicá-la. Havia um monte de coisas que eu tinha que fazer para chegar até aqui. É bom para torná-lo tão fácil quanto possível para outras pessoas para ajudá-lo. Eu também removeu um monte de pedaços estranhos. OtherFoo não é realmente relevante aqui, nem é 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);
    }
}

Sim, isso não compila. O que você espera que ele faça, exatamente? Tenha em mente que a resolução de sobrecarga é realizada no tempo de compilação , e não o tempo de execução. Como fallen888 diz, você poderia lançar e chamar o método apropriado sobrecarregado - mas qual dos dois sobrecargas que você esperaria o compilador para escolher outra forma? O que você quer fazer com Foo<string> vez de Foo<A> ou Foo<B>?

Isso tudo vai para demonstrar que os genéricos .NET são realmente significativamente diferente de modelos C ++, é claro ...

Eu não tentei, mas parece que você deve ser capaz de conseguir o que deseja, fazendo A & B visitável (por exemplo, com o padrão do visitante acíclico).

Edit: Eu não tenho certeza que você pode completar este como você está tentando. Eu tentei todos os tipos de truques para tentar chegar a este trabalho e não pode obtê-lo para compilar. O melhor que posso fazer é puxar o exterior chamada de método de minha classe genérica. Se a sua chamada de método está fora, então você pode definir especificamente o que T está no genérico. No entanto, dentro do método, em tempo de compilação, o compilador não sabe o T será que ele não sabe qual sobrecarregado método para chamada. A única maneira que eu posso ver ao redor isso é usar um interruptor para determinar o tipo de T e especificar manualmente a sobrecarga de chamada.

O melhor que posso fazer é isso, que não é exatamente o que você está depois, mas poderia ser usado para um efeito semelhante:

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

e tem saída:

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

Eu tenho quatro funções sobrecarregadas referenciando dois referenciando classes genéricas (um para int e um para cordas) e dois tipos regulares que fazem referência (um para int e um para string) e tudo funciona bem ... é isso o que você 're depois?

Edit: O problema não parece estar com o chamado dos métodos sobrecarregados, que tem a ver com o seu foreach que está tentando converter todos os itens na lista para o mesmo tipo como o primeiro, a fim de fazer referência ao método sobrecarregado. O primeiro item que não está de acordo com essa definição exata fará com que sua compilação a falhar.

Eu estava esperando encontrar uma maneira mais fácil de fazer isso, mas por agora eu estou indo com isso:

Substituir classe Foo<T> com estas classes:

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

E alterar as instâncias para

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

Esta, pelo menos, não requer dublicating o código no Foo<T>, e apenas me obriga a encaminhar os construtores.

Licenciado em: CC-BY-SA com atribuição
Não afiliado a StackOverflow
scroll top