在我的项目中,我有以下三个接口,这些接口由管理合并具有不同结构的各种业务对象的类实现。
public interface IMerger<TSource, TDestination>
{
TDestination Merge(TSource source, TDestination destination);
}
public interface ITwoWayMerger<TSource1, TSource2, TDestination>
{
TDestination Merge(TSource1 source1, TSource2 source2, TDestination destination);
}
public interface IThreeWayMerger<TSource1, TSource2, TSource3, TDestination>
{
TDestination Merge(TSource1 source1, TSource2 source2, TSource3 source3, TDestination destination);
}
这很有效,但我宁愿有一个IMerger
接口指定可变数量的TSource
参数,如下所示(下面的示例使用params
;我知道这不是有效的C#):
public interface IMerger<params TSources, TDestination>
{
TDestination Merge(params TSource sources, TDestination destination);
}
有没有办法实现这一点,或者功能相同的东西?
答案 0 :(得分:28)
你做不到。这是API的关键部分。但是,您可以在旁边做一些事情,例如接受Type[]
参数。您可能也会想到一些奇特的“流畅的API /扩展方法”这样做的方式,但说实话它可能不值得;但是像这样:
obj.Merge<FirstType>(firstData).Merge<SecondType>(secondData)
.Merge<ThirdType>(thirdData).Execute<TDestination>(dest);
或使用泛型类型推断:
obj.Merge(firstData).Merge(secondData).Merge(thirdData).Execute(dest);
每个合并步骤都可以简单地存储要执行的工作,只能由Execute
访问。
答案 1 :(得分:5)
这取决于您是否希望对象能够合并不同类型的对象。
对于同构合并,您只需要:
public interface IMerger<TSource, TDestination> {
TDestination Merge(IEnumerable<TSource> sources, TDestination destination);
}
对于异构合并,请考虑要求所有源类型派生自公共基类型:
public interface IMerger<TSourceBase, TDestination> {
TDestination Merge(IEnumerable<TSourceBase> sources, TDestination destination);
}
我认为不需要param数组,只需传入对象集合。
答案 2 :(得分:4)
params只能在结尾或参数列表中,并且是数组的语法糖:
public interface IMerger<TSources, TDestination>
{
TDestination Merge(TDestination destination, params TSource[] sources);
}
如果您想允许使用任何类型,只需使用object[]
代替TSource。
注意:当他们完成表达式内容时,MS也遇到了这个“问题”。他们提出了一堆具有不同数量的泛型参数的Action<>
和Func<>
代理,但实际上每个代理都是另一种类型。
答案 3 :(得分:0)
params
关键字仅用于方法签名,而不是用于装饰类型的东西。因此,类型仍然只是TSources
,您必须将带有params
的参数放在方法签名的最后:
public interface IMerger<TSources, TDestination> {
TDestination Merge(TDestination destination, params TSources[] sources);
}
答案 4 :(得分:0)
今天,我参与了自动化MEF的交易,这使用了一种方法来制作变量通用输入参数,封装在委托中:S
using System;
using System.Collections.Generic;
using System.Linq;
using System.Text;
using System.ComponentModel.Composition;
using System.ComponentModel.Composition.Hosting;
using System.ComponentModel.Composition.Primitives;
namespace MEFHelper
{
public static class MEFImporter
{
#region Catalog Field
private readonly static AggregateCatalog _catalog;
public static AggregateCatalog Catalog { get { return _catalog; } }
#endregion
static MEFImporter()
{
//An aggregate catalog that combines multiple catalogs
_catalog = new AggregateCatalog();
//Adds all the parts found in all assemblies in
//the same directory as the executing program
_catalog.Catalogs.Add(
new DirectoryCatalog(
System.IO.Path.GetDirectoryName(new Uri(
System.Reflection.Assembly.GetExecutingAssembly()
.CodeBase).AbsolutePath)
));
}
/// <summary>
/// Fill the imports of this object
/// </summary>
/// <param name="obj">Object to fill the Imports</param>
/// <param name="contructorParameters">MEF contructor parameters</param>
/// <remarks>Use for MEF importing</remarks>
public static void DoImport(this object obj, params MEFParam[] contructorParameters)
{
//Create the CompositionContainer with the parts in the catalog
CompositionContainer container = new CompositionContainer(Catalog, true);
//Add the contructor parameters
if (contructorParameters != null && contructorParameters.Length > 0)
{
foreach (MEFParam mefParam in contructorParameters)
if (mefParam != null && mefParam.Parameter != null) mefParam.Parameter(container);
}
//Fill the imports of this object
container.ComposeParts(obj);
}
#region MEFParam
/// <summary>
/// Creates a Mef Param to do the Import
/// </summary>
/// <typeparam name="T">Type of the value to store</typeparam>
/// <param name="value">Value to store</param>
/// <param name="key">Optional MEF label</param>
/// <returns>A MEF paramameter</returns>
/// <remarks>This retuns a MEF encapsulated parameter in a delegate</remarks>
public static MEFParam Parameter<T>(T value, string key = null)
{
Action<CompositionContainer> param;
if (string.IsNullOrWhiteSpace(key))
param = p => p.ComposeExportedValue(value);
else param = p => p.ComposeExportedValue(key, value);
return new MEFParam(param);
}
/// <summary>
/// Mef Param to do the Import
/// </summary>
public class MEFParam
{
protected internal MEFParam(Action<CompositionContainer> param)
{
this.Parameter = param;
}
public Action<CompositionContainer> Parameter { get; private set; }
}
#endregion
}
}
我使用此工具导入&amp;一般用扩展器解决MEF对象(有趣),嘲讽:你可以选择添加导入构造函数参数,这个问题在ComposeExportedValue函数中使用泛型参数,你不能在变量参数中添加这个参数功能,用这种技术,是的! 如果你试图测试:例如......
public class Factory : IDisposable
{
[Import(typeof(IRepository))]
private Repository _repository = null;
public Factory()
{
MEFImporter.DoImport(this, MEFImporter.Parameter("hello"));
}
public IRepository Repository
{
get
{
return _repository;
}
}
public void Dispose()
{
_repository = null;
}
}
---在另一个集会中
[Export(typeof(IRepository))]
public class Repository : IRepository
{
string Param;
[ImportingConstructor]
public Repository(string param)
{
//add breakpoint
this.Param = param;
}
}