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.

Foi útil?

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.

Koodos para joshua.ewer para encontrar a página man

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