Domanda

Come posso selezionare il buon metodo (nell'esempio che segue mostra 2 modi diversi che non funzionano). Stavo usando invece una variabile di tipo Object con IF e IS per fare il lavoro, ma sto cercando di evitare di usare Object e boxing / unboxing. Quindi ho pensato che Generic potesse fare il lavoro, ma sono bloccato qui.

Ecco un piccolo frammento di codice che illustra la mia domanda:

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

Aggiornamento

Non voglio interfaccia e classe. Scusate. Sapevo che non è l'obiettivo della domanda.

Casting con (ObjectType) obj non funziona ma se lo fai:

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

funziona ... perché?

E ... Non posso sovraccaricare per tutti i tipi il metodo execute perché questo metodo proviene da un'interfaccia. Questo è il motivo per cui tutti devono essere chiamati da questo metodo.

È stato utile?

Soluzione

No, non puoi farlo. I generici non funzionano come i modelli C ++: il metodo generico viene compilato una sola volta. Le uniche informazioni che il compilatore può utilizzare per la risoluzione di sovraccarico sono le informazioni di cui è a conoscenza all'interno del metodo generico, indipendentemente dal codice utilizzato.

Ad esempio per mostrarlo, ecco un po 'di codice che potrebbe non funzionare come previsto:

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

È difficile dire il modo migliore di procedere senza sapere di più su ciò che si desidera fare.

Altri suggerimenti

Hai considerato le interfacce?

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

Modificato da OP:

Questa soluzione richiederebbe di modificare tutti gli oggetti di business logic per avere questa interfaccia. Questa non è davvero una cosa da fare (nella mia situazione). E, in altre situazioni, preferisco sempre avere BusinessObject pulito che non abbia un'interfaccia non correlata con le cose aziendali. Nella mia domanda, voglio una soluzione che sia più correlata al metodo Generic / Object / Delegate per raggiungerla. Grazie. Questa risposta non sarà accettata.

  

La classe Parser ha molti metodi privati ??che vengono chiamati dal metodo execute in base al tipo di oggetto. Deve reindirizzare al buon metodo.

Il compilatore farà questo lavoro per te. Usa solo sovraccarichi.

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

Quindi, chiamato con:

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

Non c'è boxe / unboxing e viene chiamato il metodo appropriato.

Non l'ho provato, ma puoi farlo?

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

(secondo i commenti, non funziona)

o

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

(secondo commenti, opere)

So che sei preoccupato per la boxe / unboxing, quindi qui potrebbero essere coinvolti ValueTypes.

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

Supponendo che l'azione stia modificando obj, e anche supponendo che la modifica sia importante per il chiamante (motivo per cui stai restituendo il valore al chiamante). Questo codice presenta un brutto difetto pass-by-value.

Considera questo codice:

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

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

Chiamato in questo modo.

int x = p.execute(1);

x è 1, non 2.

I generici si verificano in fase di compilazione. È utilizzato al meglio quando si desidera applicare lo stesso codice a tipi diversi. Non è dinamico, quindi non ti aiuterà a passare da un metodo all'altro a seconda dei tipi di input.

Il sovraccarico si risolve come nella risposta di David B, ma si verifica anche durante il tempo di compilazione.

Il codice nel tuo aggiornamento fa la stessa cosa. Esegue il cast (dopo un attento controllo dei tipi) e quindi utilizza il sovraccarico per risolvere il metodo.

Sento che vuoi cambiare metodo in base all'input di runtime.

Potresti ottenere un comportamento più dinamico se usi 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; 
        } 

Forse è un po 'fragile, ma funziona nell'esempio.

IIRC puoi utilizzare " dove " clausola per consentire questo

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

Devo sempre su Google quello me stesso, quindi lo lascerò a quello.

modifica: lettura di alcuni commenti. Non consiglierei di chiamare il codice specifico del tipo. Piuttosto metti quel codice in una funzione virtuale e chiamalo. La firma della chiamata potrebbe richiedere molto tempo, ma è a questo che serve il completamento automatico.

Koodos a joshua.ewer per trovare la pagina man

Autorizzato sotto: CC-BY-SA insieme a attribuzione
Non affiliato a StackOverflow
scroll top