[英]How to pass a parameter into the constructor without import using MEF?
我正在与MEF合作。 我正在观看PRISM的演示MVVM RI,该程序的一部分包含以下代码:
/// <summary>
/// Factory class to create a question view model for a given question object.
/// </summary>
private static class QuestionViewModelFactory
{
private static Dictionary<Type, Func<Question, QuestionViewModel>> maps = new Dictionary<Type, Func<Question, QuestionViewModel>>()
{
{ typeof(OpenQuestion), (q) => new OpenQuestionViewModel((OpenQuestion)q) },
{ typeof(MultipleSelectionQuestion), (q) => new MultipleSelectionQuestionViewModel((MultipleSelectionQuestion)q) },
{ typeof(NumericQuestion), (q) => new NumericQuestionViewModel((NumericQuestion)q) }
};
public static QuestionViewModel GetViewModelForQuestion(Question question)
{
Func<Question, QuestionViewModel> viewModelInstanceFactory = null;
if (maps.TryGetValue(question.GetType(), out viewModelInstanceFactory))
{
return viewModelInstanceFactory(question);
}
else
{
throw new ArgumentOutOfRangeException("Could not locate a view model for question type");
}
}
}
// Note that each class derived QuestionViewModel needs a constructor parameter to be created.
public abstract class QuestionViewModel : NotificationObject
{
protected QuestionViewModel() { ... }
}
public abstract class QuestionViewModel<T> : QuestionViewModel
where T : Question
{
protected QuestionViewModel(T question) { ... }
}
在我的软件中,我需要此功能,但是现在我想通过发现来实现。
在开始时,我正在考虑创建一个自定义导出,以仅存储QuestionViewModel
并将问题类型模型存储为contractName
。 检查一下。
[MetadataAttribute]
[AttributeUsage(AttributeTargets.Class, AllowMultiple = false)]
public class ExportViewModelForProblemAttribute : ExportAttribute
{
public ExportViewModelForProblemAttribute(Type viewModelType, Type questionType)
: base(questionType.ToString(), typeof(QuestionViewModel))
{
}
}
但是然后我说,如何通过构造函数传递对象? 这个想法是传递对象q
而不使用Import。 但是我迷路了。
public class ProblemViewModelFactory
{
private readonly CompositionContainer container;
[ImportingConstructor]
public ProblemViewModelFactory(CompositionContainer container)
{
this.container = container;
}
public QuestionViewModelFactory GetQuestionViewModelFactory(Question question)
{
// what can I do to return the correspond view model with the question inside?
}
}
如何实现此映射并传递参数? 提前致谢。
使用MEF的Silverlight变体,我们可以包含一个名为ExportFactory<T, TMetadata>
。 这种类型的作用是每次我们在其上调用CreateExport()
时都会旋转该类型的新实例,但是它还包含有关该零件的一些其他信息(元数据)。
现在,您当前正在执行的操作是使用问题名称作为合同名称来导出问题视图模型。 这将不容易获得所有QuestionViewModel
类型的实例,因此,相反,您应继续导出为QuestionViewModel
并将一些元数据附加到该类型,因此在这种情况下,您需要一个name属性,以便我们可以定义我们的元数据合同为:
public interface INameMetadata
{
string Name { get; }
}
现在,让我们对export属性进行修改:
[AttributeUsage(AttributeTargets.Class, AllowMultiple = false)]
[MetadataAttribute]
public class ExportQuestionAttribute : ExportAttribute, INameMetadata
{
public ExportQuestionAttribute(string name)
: base(typeof(QuestionViewModel))
{
this.Name = name;
}
public string Name { get; private set; }
}
我已更改了导出属性类型,以仅使用type
参数调用基本构造函数,而是将name
的值存储在属性Name
。 您实际上不需要用我们的元数据协定INameMetadata
装饰您的导出属性,但我更喜欢这样做,因为这可以确保在导出时,我得到了编译时检查,以确保我提供了所有必需的元数据。
接下来,我们可以修改我们的消费者类型:
[Export]
public class ProblemViewModelFactory
{
private readonly IEnumerable<ExportFactory<Question, INameMetadata>> _questionFactories;
[ImportingConstructor]
public ProblemViewModelFactory(
[ImportMany] IEnumerable<ExportFactory<Question, INameMetadata>> questionFactories)
{
if (questionFactories == null)
throw new ArgumentNullException("questionFactories");
_questionFactories = questionFactories;
}
public QuestionViewModel GetQuestionViewModel(string name)
{
return _questionFactories
// Get matching question factories
.Where(q => q.Metadata.Name == name)
// Select the exported value
.Select(q => q.CreateExport().Value)
// First or default - what if the question doesn't exist?
.FirstOrDefault();
}
}
现在,我们以几种方式修改了该部分。
首先,我们仅接受IEnumerable<ExportFactory<QuestionViewModel, INameMetata>>
的实例,这是我们的部分工厂的集合。 其中应包含针对已导出的每种问题的工厂。 导入部分是GetQuestionViewModel
方法(我假设您要返回QuestionViewModel
,而不是QuestionViewModelFactory
)。 ExportFactory
类型完成旋转新实例的工作,并且由于它的类型为ExportFactory<QuestionViewModel, INameMetadata>
,因此它具有一个INameMetadata
类型的Metadata
属性,我们可以在创建零件之前对其进行查询。
最后一个方法将查询可用工厂集,检查每个INameMetadata
实例的Name
属性,并返回匹配的问题;如果找不到一个,则返回null
。 它还将忽略具有相同名称的多个问题,并仅选择第一个(此设计决定取决于您)。
我希望这会为您指明正确的方向。
声明:本站的技术帖子网页,遵循CC BY-SA 4.0协议,如果您需要转载,请注明本站网址或者原文地址。任何问题请咨询:yoyou2525@163.com.