C# 我可以拥有可变数量的通用参数吗?

声明:本页面是StackOverFlow热门问题的中英对照翻译,遵循CC BY-SA 4.0协议,如果您需要使用它,必须同样遵循CC BY-SA许可,注明原文地址和作者信息,同时你必须将它归于原作者(不是我):StackOverFlow 原文地址: http://stackoverflow.com/questions/1612887/
Warning: these are provided under cc-by-sa 4.0 license. You are free to use/share it, But you must attribute it to the original authors (not me): StackOverFlow

提示:将鼠标放在中文语句上可以显示对应的英文。显示中英文
时间:2020-08-06 19:23:33  来源:igfitidea点击:

Can I have a variable number of generic parameters?

c#.netgenericsparameters

提问by Richard Ev

In my project I have the following three interfaces, which are implemented by classes that manage merging of a variety of business objects that have different structures.

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

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

This works well, but I would rather have one IMergerinterface which specifies a variable number of TSourceparameters, something like this (example below uses params; I know this is not valid C#):

这很好用,但我宁愿有一个IMerger接口来指定可变数量的TSource参数,就像这样(下面的示例使用params;我知道这不是有效的 C#):

public interface IMerger<params TSources, TDestination>
{
    TDestination Merge(params TSource sources, TDestination destination);
}

It there any way to achieve this, or something functionally equivalent?

有什么方法可以实现这一点,或者功能上等效的东西?

采纳答案by Marc Gravell

You can't. That is a key part of the API. You could, however, do something around the side, such as accepting a Type[]argument. You might also think up some exotic "fluent API / extension method" way of doing it, but to be honest it probably won't be worth it; but something like:

你不能。这是 API 的关键部分。但是,您可以在旁边做一些事情,例如接受Type[]争论。您可能还会想出一些异国情调的“流畅的 API / 扩展方法”来做这件事,但老实说,这可能不值得;但类似:

obj.Merge<FirstType>(firstData).Merge<SecondType>(secondData)
     .Merge<ThirdType>(thirdData).Execute<TDestination>(dest);

or with generic type inference:

或使用泛型类型推断:

obj.Merge(firstData).Merge(secondData).Merge(thirdData).Execute(dest);

Each merge step would simple store away the work to do, only accessed by Execute.

每个合并步骤都会简单地存储要做的工作,只能由Execute.

回答by Lucero

The params can only be at the end or argument lists and is syntactic sugar for an array:

params 只能在末尾或参数列表中,并且是数组的语法糖:

public interface IMerger<TSources, TDestination>
{
  TDestination Merge(TDestination destination, params TSource[] sources);
}

If you want to allow any type to be used, just use object[]instead of TSource.

如果您想允许使用任何类型,只需使用object[]而不是 TSource。

Note:MS had this "problem" also when they did the Expression stuff. They came up with a bunch of delegates Action<>and Func<>with different numbers of generic arguments, but each delegate is another type in fact.

注意:MS 在做 Expression 的时候也有这个“问题”。他们想出了一堆委托Action<>Func<>不同数量的通用参数,但实际上每个委托都是另一种类型。

回答by Guffa

The paramskeyword is only used in a method signature, it's not something that you can decorate a type with. So, the type is still just TSources, and you have to place the parameter decorated with paramslast in the method signature:

params关键字仅用于方法签名中,您不能用它来装饰类型。因此,类型仍然只是TSources,并且您必须将用paramslast修饰的参数放置在方法签名中:

public interface IMerger<TSources, TDestination> {
    TDestination Merge(TDestination destination, params TSources[] sources);
}

回答by Christian Hayter

It depends on whether you want your objects to be able to merge objects of different types or not.

这取决于您是否希望您的对象能够合并不同类型的对象。

For a homogeneous merge, all you need is this:

对于同构合并,您只需要这样:

public interface IMerger<TSource, TDestination> {
    TDestination Merge(IEnumerable<TSource> sources, TDestination destination);
}

For a heterogeneous merge, consider requiring all source types to derive from a common base type:

对于异构合并,考虑要求所有源类型派生自一个公共基类型:

public interface IMerger<TSourceBase, TDestination> {
    TDestination Merge(IEnumerable<TSourceBase> sources, TDestination destination);
}

I don't see any need for a param array, just pass in the collection of objects.

我认为不需要 param 数组,只需传入对象集合即可。

回答by ModMa

Today, i worked in a deal to automatize MEF, this uses a way to make a variable generic input parameters, encapsulated in delegates :S

今天,我参与了一个自动化 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

    }
}

i use this tool to import & resolve MEF objects generically with a extensor (interesting), the taunt: you can add optionally the import constructor parameters, the problem of this is in the function ComposeExportedValue thats uses a generic parameter, you can't add this in a variable params in a function, with this technique, yes! if you try to test: for example...

我使用此工具通过扩展器(有趣)一般地导入和解析 MEF 对象,嘲讽:您可以选择添加导入构造函数参数,问题出在使用通用参数的 ComposeExportedValue 函数中,您不能添加this 在函数中的变量 params 中,使用这种技术,是的!如果您尝试测试:例如...

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

--- In another assembly

--- 在另一个程序集中

[Export(typeof(IRepository))]
public class Repository : IRepository
{
     string Param;

     [ImportingConstructor]
     public Repository(string param)
     {
         //add breakpoint
         this.Param = param;
     }
}