it-swarm.com.de

Kann ich eine variable Anzahl von generischen Parametern haben?

In meinem Projekt habe ich die folgenden drei Schnittstellen, die von Klassen implementiert werden, die das Zusammenführen einer Vielzahl von Geschäftsobjekten mit unterschiedlichen Strukturen verwalten.

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

Dies funktioniert gut, aber ich hätte lieber eine IMerger-Schnittstelle, die eine variable Anzahl von TSource-Parametern angibt (Beispiel unten verwendet params; ich weiß, dass dies kein gültiges C # ist):

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

Gibt es eine Möglichkeit, dies zu erreichen, oder etwas funktional Äquivalentes?

35
Richard Everett

Du kannst nicht Das ist ein wesentlicher Bestandteil der API. Sie könnten jedoch etwas an der Seite tun, beispielsweise ein Type[]-Argument akzeptieren. Sie können sich auch eine exotische "flüssige API/Erweiterungsmethode" ausdenken, aber um ehrlich zu sein, lohnt es sich wahrscheinlich nicht. aber so etwas wie:

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

oder mit generischer Typeninferenz:

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

Jeder Merge-Schritt würde die Arbeit einfach abspeichern, nur über Execute kann darauf zugegriffen werden.

28
Marc Gravell

Es hängt davon ab, ob Sie möchten, dass Ihre Objekte Objekte verschiedener Typen zusammenführen können oder nicht.

Für eine homogene Verschmelzung benötigen Sie Folgendes:

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

Erwägen Sie für eine heterogene Zusammenführung, dass alle Quelltypen von einem gemeinsamen Basistyp abgeleitet werden müssen:

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

Ich sehe keine Notwendigkeit für ein Param-Array, übergeben Sie einfach die Objekt-Sammlung.

5

Die Parameter können nur am Ende oder Argumentenlisten sein und sind syntaktischer Zucker für ein Array:

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

Wenn Sie die Verwendung eines beliebigen Typs zulassen möchten, verwenden Sie einfach object[] anstelle von TSource.

Hinweis: MS hatte dieses "Problem" auch, wenn sie den Ausdruckszeug erledigten. Sie hatten eine Reihe von Delegierten Action<> und Func<> mit unterschiedlichen Anzahlen generischer Argumente, aber jeder Delegat ist tatsächlich ein anderer Typ.

4
Lucero

Das Schlüsselwort params wird nur in einer Methodensignatur verwendet. Sie können einen Typ damit nicht dekorieren. Der Typ ist also immer noch TSources, und Sie müssen den mit params versehenen Parameter zuletzt in der Methodensignatur platzieren:

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

Heute habe ich in einem Deal an der Automatisierung von MEF gearbeitet. Dies macht es möglich, variable, generische Eingabeparameter zu erstellen, die in Delegaten eingeschlossen sind: 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

    }
}

ich benutze dieses Tool, um MEF-Objekte generisch mit einem Extensor (interessant) zu importieren und aufzulösen: Die Verspottung: Sie können optional die Importkonstruktor-Parameter hinzufügen. Das Problem liegt in der Funktion ComposeExportedValue, die einen generischen Parameter verwendet, den Sie nicht hinzufügen können dies in einer Variablen params in einer Funktion, mit dieser Technik, ja! wenn Sie versuchen zu testen: zum Beispiel ...

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 einer anderen Versammlung

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

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