Question

Quelle est une manière plus élégante d’avoir le code ci-dessous dans lequel je veux renvoyer une classe dérivée en fonction du type d’une autre classe?

            if (option_ is Rectangle)
            {
                modelInputs = new Foo();
            }
            else if (option_ is Circle)
            {
                modelInputs = new Bar();
            }
            else if (option_ is Triangle)
            {
                modelInputs = new Bar2();
            }
Était-ce utile?

La solution

Demandez à Rectangle, Circle et Triangle d’appliquer IHasModelInput:

interface IHasModelInput
{
    IModelInput GetModelInput();
}

alors vous pouvez faire

IModelInput modelInputs = option_.GetModelInput();

Autres conseils

Mon avis: votre "inélégant" chemin est bien. C'est simple, lisible et fait le travail.

Si Rectangle, Cercle et Triangle mettaient en oeuvre la fonction de fabrique nécessaire via IHasModelInput , cela aurait un coût de conception: vous avez maintenant couplé cet ensemble de classes à l'ensemble de classes IModelInput ( Foo, Bar et Bar2). Ils pourraient être dans deux bibliothèques complètement différentes, et peut-être qu'ils ne devraient pas se connaître.

Une méthode plus compliquée est ci-dessous. Cela vous donne l'avantage de pouvoir configurer votre logique d'usine lors de l'exécution.

    public static class FactoryMethod<T>  where T : IModelInput, new()
    {
        public static IModelInput Create()
        {
            return new T();
        }
    }

    delegate IModelInput ModelInputCreateFunction();

    IModelInput CreateIModelInput(object item)
    {

        Dictionary<Type, ModelInputCreateFunction> factory = new Dictionary<Type, ModelInputCreateFunction>();


        factory.Add(typeof(Rectangle), FactoryMethod<Foo>.Create);
        factory.Add(typeof(Circle),    FactoryMethod<Bar>.Create);
        // Add more type mappings here




        IModelInput modelInput;
        foreach (Type t in factory.Keys)
        {
            if ( item.GetType().IsSubclassOf(t) || item.GetType().Equals(t))
            {
                modelInput = factory[t].Invoke();
                break;
            }
        }
        return modelInput;
    }

Mais posez ensuite la question: lequel préféreriez-vous lire?

Vous pouvez placer les entrées et les sorties dans une table de hachage ou stocker les types qui créent chaque classe dans chacune des classes que vous créez, puis utiliser Activator.CreateInstance pour créer le paramètre factoryin ':

Hashtable ht = new Hashtable();
ht.Add(typeof(Rectangle), typeof(Bar));
ht.Add(typeof(Square), typeof(Bar2));

modelInputs = Activator.CreateInstance(ht[option.GetType()]);

Quoi qu'il en soit, Activator.CreateInstance est un moyen plutôt cool de faire fonctionner des usines dans .NET. Profitez et utilisez le pouvoir que je vous ai donné à bon escient, mon fils.

Vous pouvez associer un type à "option_", s'il le permet, puis en créer une instance.

J'utilise généralement une méthode d'usine telle que celle-ci lorsque je veux convertir une chaîne en un type à l'exécution, j'utilise un dictionnaire qui mappe une chaîne sur un type.

Comme celui d'un projet récent:

public class TaskFactory
{
    private Dictionary<String, Type> _taskTypes = new Dictionary<String, Type>();

    public TaskFactory()
    {
        // Preload the Task Types into a dictionary so we can look them up later
        foreach (Type type in typeof(TaskFactory).Assembly.GetTypes())
        {
            if (type.IsSubclassOf(typeof(CCTask)))
            {
                _taskTypes[type.Name.ToLower()] = type;
            }
        }
    }

    public CCTask CreateTask(XmlElement task)
    {
        if (task != null)
        {
            string taskName = task.Name;
            taskName =  taskName.ToLower() + "task";

            // If the Type information is in our Dictionary, instantiate a new instance of that task
            Type taskType;
            if (_taskTypes.TryGetValue(taskName, out taskType))
            {
                return (CCTask)Activator.CreateInstance(taskType, task);
            }
            else
            {
                throw new ArgumentException("Unrecognized Task:" + task.Name);
            }                               
        }
        else
        {
            return null;
        }
    }
}
Licencié sous: CC-BY-SA avec attribution
Non affilié à StackOverflow
scroll top