Вопрос

Какой более элегантный способ иметь приведенный ниже код, где я хочу вернуть производный класс, основанный на типе другого класса.

            if (option_ is Rectangle)
            {
                modelInputs = new Foo();
            }
            else if (option_ is Circle)
            {
                modelInputs = new Bar();
            }
            else if (option_ is Triangle)
            {
                modelInputs = new Bar2();
            }
Это было полезно?

Решение

Rectangle, Circle и Triangle должны реализовывать IHasModelInput:

interface IHasModelInput
{
    IModelInput GetModelInput();
}

тогда вы можете сделать

IModelInput modelInputs = option_.GetModelInput();

Другие советы

Мое мнение: ваш " неэлегантный " путь в порядке. Это просто, читабельно и делает свою работу.

Если бы Rectangle, Circle и Triangle реализовали необходимую фабричную функцию через IHasModelInput , это сработало бы, но это стоило разработки: теперь вы связали этот набор классов с набором классов IModelInput ( Фу, Бар и Бар2). Они могут находиться в двух совершенно разных библиотеках, и, возможно, они не должны знать друг о друге.

Более сложный метод приведен ниже. Это дает вам преимущество в том, что вы можете настроить свою фабричную логику во время выполнения.

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

Но тогда задайте вопрос: что бы вы предпочли прочитать?

Вы можете поместить входные и выходные данные в Hashtable или сохранить типы, которые создают каждый класс внутри каждого из создаваемых вами классов, а затем использовать Activator.CreateInstance для выполнения factoryin ':

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

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

В любом случае Activator.CreateInstance - это довольно крутой способ заставить фабрики работать в .NET. Наслаждайся и используй силу, которую я дал тебе мудро, сынок.

Вы можете связать тип с " option_ " ;, если это позволяет, а затем просто создать его экземпляр.

Обычно я использую фабричный метод, подобный этому, когда я хочу преобразовать строку в тип во время выполнения, я использую словарь, который отображает строку в тип.

Как из недавнего проекта:

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;
        }
    }
}
Лицензировано под: CC-BY-SA с атрибуция
Не связан с StackOverflow
scroll top