Составление частей MEF в C# как простой контейнер Funq
В Funq и, вероятно, в большинстве других IoC-контейнеров я могу просто сделать это для настройки типа:
container.Register<ISomeThing>(c => new SomeThing());
Как я могу быстро расширить MEF (или использовать существующие функции MEF), чтобы сделать то же самое без использования атрибутов.
Вот как я думал, что смогу это сделать:
var container = new CompositionContainer();
var batch = new CompositionBatch();
batch.AddExport<ISomeThing>(() => new SomeThing());
batch.AddExportedValue(batch);
container.Compose(batch);
С этим методом расширения для CompositionBatch
:
public static ComposablePart AddExport<TKey>(this CompositionBatch batch, Func<object> func)
{
var typeString = typeof(TKey).ToString();
return batch.AddExport(
new Export(
new ExportDefinition(
typeString,
new Dictionary<string, object>() { { "ExportTypeIdentity", typeString } }),
func));
}
Если я позже сделаю:
var a = container.GetExport<ISomeThing>().Value;
var b = container.GetExport<ISomeThing>().Value;
Оба экземпляра одинаковы. Как я могу заставить (настроить) их для разных экземпляров?
Если это не тот путь, как бы я сделал это в MEF?
4 ответа
Если вы не хотите использовать атрибуты, вы можете использовать этот трюк (на основе поста Марка Симанна).
Сначала создайте универсальный класс, подобный этому:
[PartCreationPolicy(CreationPolicy.NonShared)]
public class MefAdapter<T> where T : new()
{
private readonly T export;
public MefAdapter()
{
this.export = new T();
}
[Export]
public virtual T Export
{
get { return this.export; }
}
}
Теперь вы можете зарегистрировать любой класс в контейнере, например:
var registeredTypesCatalog = new TypeCatalog(
typeof(MefAdapter<Foo>),
typeof(MefAdapter<Bar>),
...);
var container = new CompositionContainer(catalog);
В качестве альтернативы, вы можете реализовать свой собственный поставщик экспорта, полученный из ExportProvider, который позволяет вам в значительной степени дублировать способ работы Funq:
var provider = new FunqyExportProvider();
provider.Register<IFoo>(context => new Foo());
var container = new CompositionContainer(provider);
Я хотел бы представить, что ключом является добавление делегата в контейнер, например:
container.AddExportedValue<Func<ISomething>>(() => new Something());
Таким образом, вы можете получить делегат и выполнить его:
var factory = container.GetExport<Func<ISomething>>();
ISomething something = factory();
Конечно, MEF (Silverlight) действительно обеспечивает нативный ExportFactory<T>
(а также ExportFactory<T,TMetadata>
тип, который поддерживает создание новых экземпляров для каждого вызова импорта. Вы можете добавить поддержку для этого, загрузив библиотеку Glen Block ExportFactory для.NET 4.0 (Desktop).
Оба экземпляра одинаковы. Как я могу заставить (настроить) их для разных экземпляров?
Просто отметьте SomeThing
класс как это:
[Export(typeof(ISomeThing)]
[PartCreationPolicy(CreationPolicy.NonShared]
public class SomeThing : ISomeThing
{
...
}
И тогда вы получите разные экземпляры, куда бы вы ни импортировали ISomeThing
,
Кроме того, вы также можете установить необходимую политику создания для импорта:
[Export(typeof(IFoo))]
public class Foo : IFoo
{
[Import(typeof(ISomeThing),
RequiredCreationPolicy = CreationPolicy.NonShared)]
public ISomething SomeThing { private get; set; }
}
В каталоге Skydrive Глена Блока, связанном с ответом Мэтью Эббота, я обнаружил нечто простое и легкое: FuncCatalog
, Загрузите его здесь: FuncCatalogExtension.
Используя несколько маленьких классов из этого проекта, я теперь мог сделать это:
var funcCatalog = new FuncCatalog();
funcCatalog.AddPart<ISomeThing>(ep => new SomeThing());
var container = new CompositionContainer(funcCatalog);
var batch = new CompositionBatch();
batch.AddExportedObject<ExportProvider>(container);
container.Compose(batch);
var a = container.GetExportedObject<ISomeThing>();
var b = container.GetExportedObject<ISomeThing>();