52 Stimmen

Kann ich eine variable Anzahl von generischen Parametern haben?

In meinem Projekt habe ich die folgenden drei Schnittstellen, die von Klassen implementiert werden, die die Zusammenführung 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);
}

Das funktioniert gut, aber ich hätte lieber eine IMerger Schnittstelle, die eine variable Anzahl von TSource Parameter, etwa so (das folgende Beispiel 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 funktionell Gleichwertiges?

33voto

Marc Gravell Punkte 970173

Das können Sie nicht. Das ist ein wesentlicher Bestandteil der API. Sie könnten jedoch etwas an der Seite machen, z. B. eine Type[] Argument. Sie könnten auch denken, einige exotische "fließende API / Erweiterung Methode" Weg, es zu tun, aber um ehrlich zu sein, es wird wahrscheinlich nicht wert sein; aber etwas wie:

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

oder mit allgemeiner Typinferenz:

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

Jeder Zusammenführungsschritt würde einfach die zu erledigende Arbeit speichern, auf die nur von Execute .

6voto

Christian Hayter Punkte 29931

Es hängt davon ab, ob Sie möchten, dass Ihre Objekte in der Lage sind, Objekte verschiedener Typen zu verschmelzen oder nicht.

Für einen homogenen Zusammenschluss brauchen Sie nur dies:

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

Bei einer heterogenen Zusammenführung ist es ratsam, dass alle Quelltypen von einem gemeinsamen Basistyp abgeleitet werden:

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

Ich sehe keine Notwendigkeit für ein Param-Array, geben Sie einfach die Sammlung von Objekten ein.

4voto

Lucero Punkte 57715

Die params können nur am Ende von Argumentlisten stehen 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 wollen, verwenden Sie einfach object[] anstelle von TSource.

Note : MS hatte dieses "Problem" auch, als sie den Expression-Kram machten. Sie kamen mit einem Haufen von Delegierten Action<> y Func<> mit einer unterschiedlichen Anzahl allgemeiner Argumente, aber jeder Delegat ist eigentlich ein anderer Typ.

0voto

Guffa Punkte 663241

El params Schlüsselwort wird nur in einer Methodensignatur verwendet, es ist nicht etwas, womit man einen Typ dekorieren kann. Der Typ ist also immer noch einfach TSources und Sie müssen den Parameter, der mit params als letztes in der Methodensignatur:

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

0voto

ModMa Punkte 489

Heute habe ich in einem Geschäft gearbeitet, um MEF zu automatisieren, dies verwendet einen Weg, um eine Variable generische Eingabeparameter zu machen, in Delegaten gekapselt :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 zu importieren und aufzulösen (interessant), der Nachteil: Sie können optional die Import-Konstruktor-Parameter hinzufügen, das Problem ist in der Funktion ComposeExportedValue, die einen generischen Parameter verwendet, Sie können dies nicht in einer Variable params in einer Funktion hinzufügen, 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;
     }
}

CodeJaeger.com

CodeJaeger ist eine Gemeinschaft für Programmierer, die täglich Hilfe erhalten..
Wir haben viele Inhalte, und Sie können auch Ihre eigenen Fragen stellen oder die Fragen anderer Leute lösen.

Powered by:

X