2009-10-23 20 views
32

En mi proyecto tengo las siguientes tres interfaces, que son implementadas por clases que administran la fusión de una variedad de objetos comerciales que tienen diferentes estructuras.¿Puedo tener un número variable de parámetros genéricos?

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

Esto funciona bien, pero yo preferiría tener una IMerger interfaz que especifica un número variable de TSource parámetros, algo como esto (ejemplo siguiente se utiliza params; sé que esto no es válida C#):

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

¿Hay alguna forma de lograr esto, o algo funcionalmente equivalente?

+0

No se puede hacer una interfaz con un número variable de argumentos, pero se puede dar el el mismo nombre para todas sus variaciones (igual que el delegado 'Func') –

+1

Si bien es solo un pseudo código inválido, tiene más sentido escribir eso como' interfaz pública IMerger { TDestination Merge (TDestination destino, params fuentes de TSource); } '. 'params' debería venir último. Pero como dice Christian Hayter, tener una clase base es la única solución – nawfal

Respuesta

26

No puede. Esa es una parte clave de la API. Sin embargo, podría hacer algo al margen, como aceptar un argumento Type[]. También podría pensar en alguna forma exótica de "API/método de extensión" exótica para hacerlo, pero para ser honesto, probablemente no valga la pena; pero algo como:

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

o con la inferencia de tipo genérico:

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

Cada paso sería fusionar sencilla tienda de distancia del trabajo que hacer, sólo se accede por Execute.

+0

Además, un enfoque de este tipo podría indicar un "orden" racional a la fusión, lo que no es cierto en nuestro escenario. –

+8

Pero entonces, también lo hace "TSource1 fuente1, TSource2 fuente2, TSource3 fuente3" de la pregunta ... –

+2

Mi pensamiento es que source1 .. sourcen indica "estas son las fuentes", mientras que una cadena de llamadas .Merge indica que las fusiones están sucediendo en ese orden específico. –

4

Los parametros sólo pueden estar en las listas finales o argumento y es el azúcar sintáctica para una matriz:

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

Si desea permitir que cualquier tipo a utilizar, sólo tiene que utilizar en lugar de object[] TSource.

Nota: MS tuvo este "problema" también cuando hicieron las cosas de Expression. Vinieron con un grupo de delegados Action<> y Func<> con diferentes números de argumentos genéricos, pero cada delegado es otro tipo de hecho.

+0

Mi ejemplo con params no es realmente válido C# ya que no puede usar params en este contexto, actualicé mi publicación para que quede más claro –

0

La palabra clave params solo se usa en una firma de método, no es algo con lo que se puede decorar un tipo. Por lo tanto, el tipo sigue siendo sólo TSources, y hay que colocar el parámetro decorado con params pasado en la firma del método:

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

¿Cómo responde esto la pregunta? ¿Cómo son 'TSource1',' TSource2' etc. equivalentes a 'TSources'? Creo que has seguido el ejemplo final de OP que publicó (el pseudo código que no funciona). – nawfal

4

Depende de si desea que los objetos sean capaces de combinar objetos de diferentes tipos o no.

Para una fusión homogénea, todo lo que necesita es la siguiente:

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

para una combinación heterogénea, considere que requiere que todos los tipos de fuentes que se derivan de un tipo base común:

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

I don' T ver cualquier necesidad de una matriz de param, simplemente pase la colección de objetos.

0

Hoy en día, trabajé en un acuerdo para automatizar MEF, este utiliza una manera de hacer que una variable parámetros de entrada genéricos, encapsulados en delegados: 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 utilizar esta herramienta para importar & objetos determinación MEF genéricamente con un extensor (interesante), la burla: puede agregar opcionalmente los parámetros del constructor de importación, el problema de esto está en la función ComposeExportedValue que usa un parámetro genérico, no puede agregar esto en una variable params en una función, con esto técnica, sí! si se intenta hacer la prueba: por ejemplo ...

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

--- En otra asamblea

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

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