Могу ли я использовать MEF для создания расширяемой фабрики? - PullRequest
2 голосов
/ 04 ноября 2011

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

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

public abstract class TheBaseClass { ... }

. Будет любое количество подклассов, производных от базового класса:

public class OneSubClass : TheBaseClass { ... }

public class AnotherSubClass : TheBaseClass { ... }

Во время выполнения мне нужнофабрика, которую я могу вызвать, передав значение 'key', чтобы получить конкретный экземпляр подкласса, возвращаемый как:

var key = "AnotherSubClass";
TheBaseClass instance = TheFactory.CreateInstance(key);

В Castle и Unity я мог зарегистрировать тип с ключом в качестве имени ииспользуйте это значение для поиска при попытке разрешить экземпляр из контейнера.Я думал, что смогу выполнить то же самое с помощью ExportMetadata, но пока не знаю, как это сделать.

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

Любые идеи?

Ответы [ 2 ]

2 голосов
/ 04 ноября 2011

Я бы предложил использовать строго типизированные имена, чтобы избежать опечаток.

Для этого сначала нужно создать перечисление, которое вы будете использовать в качестве ключа:

public enum DerivedClassesKeyEnum
{
    ONE,
    TWO
}

Затем вы создаете пользовательский атрибут:

[MetadataAttribute]
[AttributeUsage(AttributeTargets.Class)]
public class DirivedBaseExportAttribute : ExportAttribute
{
    public DirivedBaseExportAttribute()
        :base(typeof(TheBaseClass))
    { }

    public DerivedClassesKeyEnum DerivedClassId { get; set; }
}

Затем вы применяете этот атрибут к вашим производным классам:

[DirivedBaseExport(DerivedClassId=DerivedClassesKeyEnum.ONE)]
public class OneSubClass : TheBaseClass
{

}

В части, которая будет импортировать эти классы, вы объявляете интерфейс:

public interface IDerivedClassMetadata
{
    DerivedClassesKeyEnum DerivedClassId{get;}
}

И последний бит в вашем FactoryClass:

public class TheFactory
{
    [ImportMany]
    public static IEnumerable<Lazy<TheBaseClass, IDerivedClassMetadata>> DerivedClasses { get; set; }

    public static TheBaseClass CreateInstance(DerivedClassesKeyEnum id)
    {
        return DerivedClasses.Single(c => c.Metadata.DerivedClassId == id).Value;
    }

}

Это упрощенный код только для иллюстрации использования.

2 голосов
/ 04 ноября 2011

это работает так же, как и в MEF, как и в других контейнерах IoC, которые вы упомянули.

[Export("one", typeof(TheBaseClass)]
public class OneSubClass : TheBaseClass { ... }

[Export("two", typeof(TheBaseClass)]
public class AnotherSubClass : TheBaseClass { ... }

Я назначил здесь «ключи» "one" и "two", но, конечноВы можете использовать все, что вам нравится.

Затем вы используете эту клавишу в сочетании с GetExport():

var catalog = new TypeCatalog(typeof(OneSubClass), typeof(AnotherSubClass));
var container = new CompositionContainer(catalog);

var two = container.GetExport<TheBaseClass>("two");
var value = two.Value;

Пара заметок;не забудьте освободить экспорт, который вы получаете из контейнера, используя, например, container.ReleaseExport(two).

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

...