Подтвердить что ты не робот

Могу ли я иметь переменное число общих параметров?

В моем проекте у меня есть следующие три интерфейса, которые реализуются классами, которые управляют слиянием различных бизнес-объектов, которые имеют разные структуры.

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

Это там какой-нибудь способ добиться этого, или что-то функционально эквивалентное?

4b9b3361

Ответ 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;
     }
}