Могу ли я иметь переменное число общих параметров?
В моем проекте у меня есть следующие три интерфейса, которые реализуются классами, которые управляют слиянием различных бизнес-объектов, которые имеют разные структуры.
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
; я знаю, что это недопустимо С#):
public interface IMerger<params TSources, TDestination>
{
TDestination Merge(params TSource sources, TDestination destination);
}
Это там какой-нибудь способ добиться этого, или что-то функционально эквивалентное?
Ответы
Ответ 1
Вы не можете. Это ключевая часть 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
.
Ответ 2
Параметры могут быть только в конце или списке аргументов и являются синтаксическим сахаром для массива:
public interface IMerger<TSources, TDestination>
{
TDestination Merge(TDestination destination, params TSource[] sources);
}
Если вы хотите разрешить использование любого типа, просто используйте object[]
вместо TSource.
Примечание. У MS была эта "проблема", когда они делали материал Expression. Они придумали кучу делегатов Action<>
и Func<>
с разными номерами общих аргументов, но каждый делегат - это другой тип.
Ответ 3
Это зависит от того, хотите ли вы, чтобы ваши объекты могли объединять объекты разных типов или нет.
Для однородного слияния все, что вам нужно, это:
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, просто передаю в коллекцию объектов.
Ответ 4
Ключевое слово params
используется только в сигнатуре метода, это не то, что вы можете украсить типом. Таким образом, тип все еще просто TSources
, и вы должны поместить параметр, украшенный params
последним в сигнатуре метода:
public interface IMerger<TSources, TDestination> {
TDestination Merge(TDestination destination, params TSources[] sources);
}
Ответ 5
Сегодня я работал над сделкой по автоматизации 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
}
}
Я использую этот инструмент для импорта и разрешения объектов MEF в целом с помощью экстензора (интересного), taunt: вы можете дополнительно добавить параметры конструктора импорта, проблема в том, что это в функции ComposeExportedValue, которая использует общий параметр, вы можете 't добавить это в переменные 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;
}
}
--- В другой сборке
[Export(typeof(IRepository))]
public class Repository : IRepository
{
string Param;
[ImportingConstructor]
public Repository(string param)
{
//add breakpoint
this.Param = param;
}
}