Универсальный метод Factory для создания экземпляра производного класса из базового класса

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

public abstract class ServiceClientBase : IServiceClient
{
}

public abstract class Channel : ServiceClientBase
{
}

public class ChannelImpl : Channel
{
}

public class ServiceClientFactory
{
    public T GetService<T>() where T : class, IServiceClient
    {
        // Use reflection to create the derived type instance
        T instance = typeof(T).GetConstructor(BindingFlags.NonPublic | BindingFlags.Instance, null, new Type[] { typeof(string) }, null).Invoke(new object[] { endPointUrl }) as T;
    }
}

Использование:

Channel channelService = factory.GetService<Channel>();

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


person koder    schedule 23.05.2012    source источник
comment
Что ж, предположим, у вас есть две разные конкретные реализации абстрактного класса - как бы вы тогда хотели принять решение?   -  person Jon Skeet    schedule 23.05.2012
comment
Это именно то, что меня беспокоит. Я знаю, что в моем конкретном случае будет только одна конкретная реализация. Но все же я искал более элегантный способ решения проблемы.   -  person koder    schedule 23.05.2012
comment
Вы пытаетесь создать прокси-сервер WCF? Мне был бы интересен полный контекст, потому что вы, возможно, пытаетесь воссоздать колесо.   -  person Bronumski    schedule 23.05.2012
comment
@Bronumski Нет, я не пытаюсь создать прокси WCF ... имя класса канала может вводить в заблуждение. Я просто пытаюсь создать для клиентов, использующих API, возможность создать экземпляр конкретного класса, используя параметр универсального типа абстрактного базового типа.   -  person koder    schedule 23.05.2012


Ответы (2)


Хотя вы уверены, что существует только одна реализация, и предполагаете, что она находится в одной сборке, вы можете найти ее с помощью отражения. Например:

Type implementationType = typeof(T).Assembly.GetTypes()
                                   .Where(t => t.IsSubclassOf(typeof(T))
                                   .Single();
return (T) Activator.CreateInstance(implementationType);

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

Если существует несколько классов реализации, вам нужно подумать об альтернативе - одним из вариантов будет атрибут абстрактного класса, указывающий, какую реализацию использовать, если это возможно. (Трудно дать хорошие варианты без дополнительного контекста.)

person Jon Skeet    schedule 23.05.2012

Похоже, вы пытаетесь заново изобрести контейнер IOC. Взгляните, например, на Autofac. Вы должны зарегистрировать свои конкретные типы в контейнере IOC, а затем запросить их через интерфейс.

person Ian Mercer    schedule 23.05.2012
comment
Это действительно может быть стоящим вариантом. Я как раз работал над POC. Итак, я пытался придумать простую в работе альтернативу. Я бы определенно рассмотрел возможность использования IOC для разрешения зависимости. - person koder; 23.05.2012