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
Can I have a variable number of generic parameters?
提问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 IMerger
interface which specifies a variable number of TSource
parameters, 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 params
keyword 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 params
last in the method signature:
该params
关键字仅用于方法签名中,您不能用它来装饰类型。因此,类型仍然只是TSources
,并且您必须将用params
last修饰的参数放置在方法签名中:
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;
}
}