我可以使用可变数量的通用参数吗?

时间:2009-10-23 11:40:27

标签: c# .net generics parameters

在我的项目中,我有以下三个接口,这些接口由管理合并具有不同结构的各种业务对象的类实现。

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);
}

有没有办法实现这一点,或者功能相同的东西?

5 个答案:

答案 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;
     }
}