Pregunta

¿Cómo puedo seleccionar el buen método? (En el ejemplo a continuación, muestro 2 formas diferentes que no funcionan). Estaba usando en lugar de una variable de tipo Object con un IF e IS para hacer el trabajo, pero estoy tratando de evitar usar Object y boxing / unboxing. Entonces pensé que Generic podría hacer el trabajo, pero estoy atrapado aquí.

Aquí hay un pequeño fragmento de código que ilustra mi pregunta:

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

Actualizar

No quiero interfaz y clase. Lo siento. Sabía que no es el objetivo de la pregunta.

Transmitir con (ObjectType) obj no funciona, pero si lo hace:

        if (obj is ObjectType1)
            this.action(obj as ObjectType1);
        else if (obj is ObjectType2)
            this.action(obj as ObjectType1);

funciona ... ¿por qué?

Y ... No puedo sobrecargar para todos los tipos el método de ejecución porque este método es de una interfaz. Es por eso que todos deben llamarse desde este método.

¿Fue útil?

Solución

No, no puedes hacer esto. Los genéricos no funcionan como las plantillas de C ++: el método genérico se compila solo una vez. La única información que el compilador puede usar para la resolución de sobrecarga es la información que conoce dentro del método genérico, independientemente del código que la use.

Como ejemplo para mostrar esto, aquí hay un código que puede no funcionar como espera que lo haga:

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

Es difícil decir la mejor manera de proceder sin saber más sobre lo que quiere hacer.

Otros consejos

¿Has considerado las 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 solución requeriría cambiar todos los objetos de lógica de negocios para tener esta interfaz. Esto realmente no es algo que hacer (en mi situación). Y, en otra situación, siempre prefiero tener BusinessObject limpio que no tenga una interfaz no relacionada con cosas de negocios. En mi pregunta, quiero una solución que esté más relacionada con el método Genérico / Objeto / Delegado para lograrlo. Gracias. Esta respuesta no será aceptada.

  

La clase Parser tiene muchos métodos privados que son llamados por el método execute según el tipo de objeto. Necesita redirigir al buen método.

El compilador hará este trabajo por usted. Solo usa 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 { }

Luego, llamado con:

Parser p = new Parser();
p.action(new ObjectType1());
p.action(new ObjectType2());

No hay boxing / unboxing, y se llama al método apropiado.

No lo he probado, pero ¿puedes hacer esto?

public T execute<T>(T obj)
{
    this.action((T)obj);
    return obj;
}

(según los comentarios, no funciona)

o

public T execute<T>(T obj)
{
    this.action(obj as T);
    return obj;
}

(según comentarios, funciona)

Sé que te preocupa el boxeo / unboxing, por lo que podría haber ValueTypes involucrados aquí.

public T execute<T>(T obj)   
{        
    this.action(obj);
    return obj;
}

Suponiendo que la acción está modificando obj, y también suponiendo que esa modificación es importante para la persona que llama (es por eso que está devolviendo el valor a la persona que llama). Este código tiene un desagradable defecto de paso por valor.

Considere este código:

    public int execute(int obj)   
    {        
        this.action(obj);
        return obj;
    }

    public void action(int obj)
    {
        obj = obj + 1;
    }

Llamado de esta manera.

int x = p.execute(1);

x es 1, no 2.

Los genéricos ocurren en tiempo de compilación. Se utiliza mejor cuando desea que se aplique el mismo código a diferentes tipos. No es dinámico, por lo que no le ayudará a cambiar entre métodos según los tipos de entrada.

La resolución de sobrecarga como en la respuesta de David B funciona, pero también ocurre durante el tiempo de compilación.

El código en su actualización hace lo mismo. Transmite (después de una cuidadosa verificación de los tipos) y luego utiliza la sobrecarga para resolver el método.

Siento que desea cambiar los métodos basados ??en la entrada de tiempo de ejecución.

Podría obtener un comportamiento más dinámico si usara Reflection.

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

Quizás sea un poco frágil, pero funciona en el ejemplo.

IIRC puede usar el " donde " cláusula para permitir esto

public T execute<T>(T obj) where : /* somthing */
{
}

Siempre tengo que buscarlo en Google, así que lo dejaré así.

editar: leer algunos comentarios. No recomendaría llamar al código específico del tipo. Más bien ponga ese código en una función virtual y llame a eso. La firma de la llamada puede ser larga, pero para eso está la autocompletar.

Koodos a joshua.ewer para encontrar la página de manual

Licenciado bajo: CC-BY-SA con atribución
No afiliado a StackOverflow
scroll top