C # genérica e método
Pergunta
Como posso selecionar o método bom (eu tenho no exemplo abaixo mostra 2 differents forma que não funciona). Eu estava usando, em vez de uma variável do tipo de objeto com um IF e IS para fazer o trabalho, mas eu estou tentando evitar o uso de objetos e boxe / unboxing. Então eu pensei que genérico poderia fazer o trabalho, mas eu estou preso aqui.
Aqui está um pequeno trecho de código que ilustram a minha pergunta:
class Program
{
static void Main(string[] args)
{
Parser p = new Parser();
ObjectType1 o1 = new ObjectType1();
p.execute(o1);
Console.Read();
}
}
class Parser
{
public T execute<T>(T obj)
{
/*
if (obj is ObjectType1)
this.action((ObjectType1)obj);
else if (obj is ObjectType2)
this.action((ObjectType2)obj);
*/
this.action(obj);
return obj;
}
private void action(ObjectType1 objectType1)
{
Console.WriteLine("1");
}
private void action(ObjectType2 objectType2)
{
Console.WriteLine("2");
}
}
class ObjectType1
{
}
class ObjectType2
{
}
Atualização
Eu não quero interface e classe. Desculpe. Eu sabia que ele não é o objetivo da questão.
Fundição com (ObjectType) obj não funciona, mas se você fizer isso:
if (obj is ObjectType1)
this.action(obj as ObjectType1);
else if (obj is ObjectType2)
this.action(obj as ObjectType1);
funciona ... por quê?
E ... Eu não posso sobrecarregar para todo o tipo do método execute porque este método é de uma interface. É por isso que toda a necessidade de ser chamado a partir deste método.
Solução
Não, você não pode fazer isso. Genéricos não funcionam como modelos C ++ - o método genérico é compilado apenas uma vez. A única informação que o compilador pode usar para resolução de sobrecarga é a informação que sabe sobre dentro do método genérico, independentemente do que o usa código.
Como um exemplo para mostrar isso, aqui está um pouco de código que não pode funcionar como você espera que ele:
using System;
class Test
{
static void Main()
{
string x = "hello";
string y = string.Copy(x);
Console.WriteLine(x==y); // Overload used
Compare(x, y);
}
static void Compare<T>(T x, T y) where T : class
{
Console.WriteLine(x == y); // Reference comparison
}
}
É difícil dizer a melhor maneira de proceder sem saber mais sobre o que você quer fazer.
Outras dicas
Você considerou as interfaces?
interface IAction
{
void action();
}
class ObjectType1 : IAction
{
void action() {
Console.WriteLine("1");
}
}
class ObjectType2 : IAction
{
void action() {
Console.WriteLine("2");
}
}
class Parser
{
public IAction execute(IAction obj)
{
obj.action();
return obj;
}
}
Editado por OP:
Esta solução exigiria a mudar toda a lógica de negócios Objeto de ter esta interface. Esta não é realmente uma coisa a fazer (na minha situação). E, em outra situação, eu sempre preferem ter BusinessObject limpa que não têm interface não relacionados com o material de negócios. Na minha pergunta, eu quero uma solução que está mais relacionada com Generic / objeto / método Delegado para alcançá-lo. THX você. Esta resposta não serão aceitos.
A classe Parser tem um monte de método privado que são chamados pelo método de execução, dependendo do tipo de objeto. Ele precisa redirecionar para o bom método.
O compilador vai fazer este trabalho para você. Basta usar sobrecargas.
class Parser
{
public ObjectType1 action(ObjectType1 objectType1)
{
Console.WriteLine("1");
return objectType1;
}
public ObjectType2 action(ObjectType2 objectType2)
{
Console.WriteLine("2");
return objectType2;
}
}
class ObjectType1 { }
struct ObjectType2 { }
Então, chamado com:
Parser p = new Parser();
p.action(new ObjectType1());
p.action(new ObjectType2());
Não há boxing / unboxing, e o método apropriado é chamado.
Eu não tentei isso, mas você pode fazer isso?
public T execute<T>(T obj)
{
this.action((T)obj);
return obj;
}
(de acordo com os comentários, não trabalho)
ou
public T execute<T>(T obj)
{
this.action(obj as T);
return obj;
}
(de acordo com comentários, funciona)
Eu sei que você está preocupado sobre boxe / unboxing, por isso não poderia ser valuetypes envolvido aqui.
public T execute<T>(T obj)
{
this.action(obj);
return obj;
}
Supondo que a ação está modificando obj, e também supondo que a modificação é importante para o chamador (que é por isso que você está retornando o valor de volta para o chamador). Este código tem um defeito desagradável passagem por valor.
Considere este código:
public int execute(int obj)
{
this.action(obj);
return obj;
}
public void action(int obj)
{
obj = obj + 1;
}
Chamado desta forma.
int x = p.execute(1);
x é 1, não 2.
Os genéricos acontece em tempo de compilação. É melhor usado quando você quer o mesmo código para aplicar a diferentes tipos. Não é dinâmico, por isso não vai ajudá-lo a alternar entre os métodos dependendo os tipos de entrada.
A sobrecarga resolver como em obras de resposta de David B, mas também acontece durante o tempo de compilação.
O código na sua atualização faz a mesma coisa. Ela lança (após verificação cuidadosa de tipos) e então usa sobrecarga para resolver o método.
Eu sinto que você quiser mudar os métodos com base na entrada de tempo de execução.
Você pode obter um comportamento mais dinâmico se você usou Reflexão.
public object execute(object obj)
{
MethodInfo m = typeof(Parser).GetMethod(
"action",
BindingFlags.Instance | BindingFlags.NonPublic,
null,
new Type[] { obj.GetType() },
null);
m.Invoke(this, new object[] { obj });
return obj;
}
É talvez um pouco frágil, mas funciona no exemplo.
IIRC você pode usar a cláusula "onde" para permitir que este
public T execute<T>(T obj) where : /* somthing */
{
}
Eu sempre tenho que Google que um meu eu, então eu vou deixar por isso mesmo.
edit: ler alguns comentários. Eu não aconselharia chamando código específico tipo. Em vez colocar esse código em uma função virtual e chamar isso. A assinatura de chamadas podem começar por muito tempo, mas é o que completa auto é para.