Question

Comment puis-je choisir la bonne méthode (dans l'exemple ci-dessous, je montre 2 façons différentes qui ne fonctionnent pas). J'utilisais au lieu d'une variable de type Object avec un IF et un IS pour faire le travail, mais j'essaie d'éviter d'utiliser Object et de boxing / unboxing. Donc, je pensais que le générique pourrait faire le travail mais je suis coincé ici.

Voici un petit extrait de code qui illustre ma question:

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

Mettre à jour

Je ne veux pas d’interface ni de classe. Pardon. Je savais que ce n'était pas le but de la question.

La diffusion avec (ObjectType) obj ne fonctionne pas, mais si vous le faites:

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

ça marche ... pourquoi?

Et ... je ne peux pas surcharger pour tous les types de la méthode execute car cette méthode est issue d'une interface. C’est pourquoi tous doivent être appelés à partir de cette méthode.

Était-ce utile?

La solution

Non, vous ne pouvez pas faire ça. Les génériques ne fonctionnent pas comme les modèles C ++ - la méthode générique est compilée une seule fois. Les seules informations que le compilateur peut utiliser pour la résolution de la surcharge sont celles qu’il connaît dans la méthode générique, quel que soit le code utilisé.

Comme exemple pour illustrer ceci, voici un peu de code qui pourrait ne pas fonctionner comme prévu:

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

Il est difficile de dire quelle est la meilleure façon de procéder sans en savoir plus sur ce que vous voulez faire.

Autres conseils

Avez-vous envisagé des 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;
   }
}

Edité par OP:

Cette solution nécessiterait de modifier tous les objets de logique métier pour qu'ils disposent de cette interface. Ce n'est vraiment pas une chose à faire (dans ma situation). Et, dans d'autres situations, je préfère toujours avoir BusinessObject propre qui n'a pas d'interface non liée aux affaires. Dans ma question, je souhaite une solution plus liée à la méthode Generic / Object / Delegate pour l’atteindre. Merci. Cette réponse ne sera pas acceptée.

  

La classe Parser a beaucoup de méthodes privées qui sont appelées par la méthode execute en fonction du type d'objet. Il doit rediriger vers la bonne méthode.

Le compilateur fera ce travail pour vous. Il suffit d'utiliser des surcharges.

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

Ensuite, appelé avec:

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

Il n'y a pas de boxing / unboxing, et la méthode appropriée est appelée.

Je ne l'ai pas essayé, mais pouvez-vous le faire?

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

(selon les commentaires, ne fonctionne pas)

ou

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

(selon les commentaires, fonctionne)

Je sais que vous êtes préoccupé par la boxe / le déballage, il pourrait donc y avoir des ValueTypes impliqués ici.

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

Supposons que cette action modifie obj et que cette modification soit importante pour l'appelant (c'est pourquoi vous renvoyez la valeur à l'appelant). Ce code a un vilain défaut au niveau de la valeur par passage.

Considérez ce code:

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

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

Appelé de cette manière.

int x = p.execute(1);

x est 1, pas 2.

Les génériques se produisent au moment de la compilation. Il est préférable de l'utiliser lorsque vous souhaitez que le même code s'applique à différents types. Ce n'est pas dynamique, donc cela ne vous aidera pas à changer de méthode en fonction du type d'entrée.

La surcharge comme dans la réponse de David B fonctionne, mais se produit également pendant la compilation.

Le code de votre mise à jour fait la même chose. Il jette (après vérification minutieuse des types) puis utilise la surcharge pour résoudre la méthode.

Je pense que vous souhaitez changer de méthode en fonction de l'entrée d'exécution.

Vous pourriez obtenir un comportement plus dynamique si vous utilisiez 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; 
        } 

C'est peut-être un peu fragile, mais ça marche dans l'exemple.

IIRC, vous pouvez utiliser l'option " where " clause permettant cette

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

Je dois toujours utiliser Google pour moi, donc je vais en rester là.

modifier: lire des commentaires. Je ne conseillerais pas le code spécifique au type d'appel. Mettez plutôt ce code dans une fonction virtuelle et appelez-le. La signature de l’appel peut être longue, mais c’est à cela que s’ajoute la saisie automatique.

Koodos à joshua.ewer pour trouver la page de manuel

Licencié sous: CC-BY-SA avec attribution
Non affilié à StackOverflow
scroll top