Como posso sobrecarregar um método C # por instâncias específicas de um tipo genérico
-
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.
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.