2010-04-30 36 views
10

¿Hay alguna manera de hacer algo como esto en C#?¿Hay alguna manera de hacer algo como esto en C#?

public void DoSomething(string parameterA, int parameterB) 
{ 

} 

var parameters = ("someValue", 5); 
DoSomething(parameters); 
+1

Simplemente curioso, ¿tiene sentido esto? – Alex

+0

¿En realidad sí ...? –

+0

Realmente no puedo agregar más de lo que ya ha sido agregado por las otras respuestas, pero estoy curioso por qué quieres hacer esto. No me refiero a nada negativo por eso, es solo cuando veo gente haciendo algo fuera de lo común (como este), no puedo evitar preguntarme por qué están adoptando el enfoque que son. – senfo

Respuesta

21

Cerrar, pero desafortunadamente sólo con objeto (por lo que obtener un montón de boxeo/unboxing)

public void DoSomething(params object[] parameters) 
{ 

} 

var parameters = new object[]{"someValue", 5}; 
DoSomething(parameters); // this way works 
DoSomething("someValue", 5); // so does this way 
+0

+1 para acercarse lo más posible sin recurrir a la reflexión (o delegar la invocación). – stakx

+1

Pensé que él dijo que un requisito no era cambiar la firma del método? Incluso puso ese requerimiento en negrita, jajaja, ¡luego marca esto como la respuesta! – AaronLS

+0

@ AaronLS: Sí, pero esta es la única forma, aunque no es perfecta ... –

4

nop - esto no es posible.

8

Puede invocar a través de la reflexión, pero que va a incurrir en ciertos gastos indirectos:

using System; 
using System.Reflection; 

namespace SO2744885 
{ 
    class Program 
    { 
     public void DoSomething(string parameterA, int parameterB) 
     { 
      Console.Out.WriteLine(parameterA + ": " + parameterB); 
     } 

     static void Main(string[] args) 
     { 
      var parameters = new object[] { "someValue", 5 }; 
      Program p = new Program(); 
      MethodInfo mi = typeof(Program).GetMethod("DoSomething"); 
      mi.Invoke(p, parameters); 
     } 
    } 
} 

Por supuesto, si se puede cambiar la firma del método para tomar una matriz, que va a trabajar así, pero eso se verá peor en mi opinión.

+2

¿No podría simplemente 'Acción m = p.DoSomething; m.DynamicInvoke (parámetros); '? –

+0

* gah *, sí, ¡esa es una solución mejor! día largo, muy poco café, yaddayaddayadda. Por favor, vota la respuesta de los gastadores. –

1

Si son todos del mismo tipo, sí, se puede hacer algo para este efecto:

public void Print(params string[] args) { 
    foreach (string arg in args) { 
    Console.WriteLine(arg); 
    } 
} 

// ... 

Print("apple", "banana"); 
Print("apple", "banana", "cherry"); 
Print("apple", "banana", "cherry", "doughnut"); 

De lo contrario, no, no puede expandir los parámetros en su lugar sin usar el reflejo. C# no tiene el equivalente de Ruby's splat operator.

2

Usted puede hacer esto (.NET 4.0):

var parameters = Tuple.Create("someValue", 5); 

DoSomething(parameters.Item1, parameter.Item2); 
+0

Creo que esta respuesta pasa por alto. Esto estaría más cerca de lo que se ha preguntado si la función 'DoSomething' aceptaría ** tuple ** y" desempaquetarlo "a través de los parámetros originales. (Pero C# no tiene esa característica). – stakx

+0

Estoy de acuerdo, esto no resuelve el problema en absoluto (por interesante que sea). –

+0

@stakx: La adición de esta "característica" es bastante sencilla usando un método de extensión, sin embargo. Echa un vistazo a la respuesta de Henrik. –

10

No hay necesidad de utilizar la reflexión si la primera tienda como delegado, pero sí requiere una fuerte declaración del delegado.

public void DoSomething(string parameterA, int parameterB) 
{ 
    Console.WriteLine(parameterA+" : "+parameterB); 
} 
void Main() 
{ 

    var parameters = new object[]{"someValue", 5}; 
    Action<string,int> func=DoSomething; 
    func.DynamicInvoke(parameters); 

} 

... y puede olvidarse del tipo de tiempo de compilación/comprobación de cordura de la lista de parámetros. Probablemente algo malo.

0

"var" solo representa un tipo particular, es efectivamente una abreviatura para escribir un nombre de tipo. En lo anterior, no especificas ningún tipo. La única manera de hacer esto es hacer una clase de parámetros para representar las entradas a granel ...

public void DoSomething(Parameters param) 
{ 
... 
} 

var param = new Parameters("someValue", 5); 
DoSomething(param); 

... pero esto sólo va a ser útil en circunstancias específicas. Puede hacer que múltiples constructores de Parámetros representen diferentes arreglos de parámetros, pero la función que está llamando solo tomará UNA entrada, el objeto Parámetros. Entonces con esto estás minando la capacidad de sobrecargar una función.

Así que, en resumen, no. =)

2

que puede hacer:

public void DoSomething(string parameterA, int parameterB) 
{ 

} 

var func = (Action)(() => DoSomething("someValue", 5)); 
func(); 
+0

Jaja, me gusta eso: 'var func = (Action) (...)'. Un fanático de la mala dirección, ¿verdad? –

+0

@Dan Tao, ¿qué es tan gracioso? – Andrey

+1

Tienes 'Action', que has llamado' func', y lo has declarado con 'var'. Para mí eso es como escribir 'var float = (double) 2.5;' ... OK, OK, tal vez no es tan gracioso. Pero usted * podría haber * simplemente escrito 'Action action =() => DoSomething (" someValue ", 5);' –

3

Tal vez de esta manera es más "limpia":

// standard method calling 
DoSomething("Johny", 5); 
// since C# 4.0 you can used "named parameters" 
DoSomething(name: "Johny", number: 5); 
// calling with parameter's "container" 
DoSomething(new DoSomethingParameters("Johny", 5)); 
// calling with parameter's "container" 
DoSomething(new DoSomethingParameters{ Name = "Johny", Number = 5 }); 
// calling with callback for parameters initialization 
DoSomething(p => { p.Name = "Johny"; p.Number = 5; }); 

// overload of DoSomething method with callback, which initialize parameters 
public void DoSomething(Action<DoSomethingParameters> init) { 
    var p = new DoSomethingParameters(); 
    init(p); 
    DoSomething(p); 
} 

// overload of DoSomething method for calling with simple parameters 
public void DoSomething(string name, int number) { 
    var p = new DoSomethingParameters(name, number); 
    DoSomething(p); 
} 
// the "main executive" method which is "doing the work" 
// all posible parameters are specified as members of DoSomethingParameters object 
public void DoSomething(DoSomethingParameters p) { /* ... */ } 

// specify all parameters for DoSomething method 
public class DoSomethingParameters { 
    public string Name; 
    public int Number; 

    public DoSomethingParameters() { } 
    public DoSomethingParameters(string name, int number) { 
     this.Name = name; 
     this.Number = number; 
    } 
} 
+1

Agradable, agregue un poco de texto explicativo también :) –

+1

@Filip: ok, como desee :) – TcKs

+2

No como comentarios !! ;) –

0

¿Qué hay de esto en.NET 4 (por el bien de la curiosidad)

public void DoSomething(string parameterA, int parameterB) 
{ 
} 

public void Helper(dynamic parameter) 
{ 
    DoSomething(parameter.Parameter1, parameter.Parameter2); 
} 

var parameters = new {Parameter1="lifeUniverseEverything", Parameter2=42}; 

Helper(parameters); 
3

Inspirado por la respuesta de Steven:

static public void Execute<T1, T2>(this Tuple<T1, T2> parameters, Action<T1, T2> action) 
{ 
    action(parameters.Item1, parameters.Item2); 
} 

var parameters = Tuple.Create("someValue", 5); 
parameters.Execute(DoSomething); 
+0

Iba a sugerir lo mismo. Excepto, creo que tiene más sentido hacer que 'Execute' sea un método de extensión en' Action', ¿no? Entonces: '((Acción ) DoSomething) .Execute (parameters);' Es conveniente que el suyo no requiera el lanzamiento a 'Action', pero también es un poco extraño pensar en los parámetros que ejecutan una acción. –

+0

+1 Muy agradable. – Steven

1

Si no desea cambiar la firma del método por qué no declarar un nuevo método con la firma apropiada y usar eso como un proxy. Al igual que

public void DoSomething(string parameterA, int parameterB) 
{ 
    // Original do Something 
} 

public void DoSomething(object[] parameters) 
{ 
    // some contract check whether the parameters array has actually a good signature 
    DoSomething(parameters[0] as string,(parameters[1] as int?).Value); 
} 

var parameters = new object[]{"someValue", 5}; 
DoSomething(parameters); 

También puede probar algunas de las cosas LinFu.Reflection proporciona, como Enlace en tiempo. Con ella se puede hacer algo como esto:

var dosomethingobject = new ObjectThatHasTheDoSomething(); 
DynamicObject dynamic = new DynamicObject(dosomethingobject); 

var parameters = new object[]{"someValue", 5}; 
dynamic.Methods["DoSomething"](parameters); 

Para ello es necesario que el método es DoSomething dentro de un objeto.

14

Hoy no, no. Actualmente estamos creando prototipos exactamente de esa característica para una posible versión futura hipotética de C#.

Si puede proporcionar una razón realmente increíble por la que desea esta característica, eso sería un punto para realmente salir de la creación de prototipos y en una posible futura versión hipotética. ¿Cuál es tu escenario increíble que motiva esta característica?

(Recuerde, las especulaciones de Eric sobre posibles futuras versiones hipotéticas de C# son para propósitos de entretenimiento solamente y no deben ser interpretados como promesas que alguna vez habrá una liberación o que se tiene un conjunto de características en particular.)

+1

+1 para el final :) – RCIX

+0

En segundo lugar que votan por lol. –

3

Me gusta la respuesta de Henrik, excepto que impone una sintaxis algo extraña: los parámetros llaman a un método en sí mismos. Lo haría al revés. El único problema con este enfoque es que le hace lanzar explícitamente un método a un delegado.

De todos modos, aquí está la idea básica:

// wrapped code to prevent horizontal overflow 
public static void Execute<T1, T2> 
(this Action<T1, T2> action, Tuple<T1, T2> parameters) { 
    action(parameters.Item1, parameters.Item2); 
} 

Y así sucesivamente (para más T s).

Uso:

var parameters = Tuple.Create("Hi", 10); 

Action<string, int> action = DoSomething; 

action.Execute(parameters); 

También puede hacer esto con un valor de retorno:

// wrapped code to prevent horizontal overflow 
public static TResult Return<T1, T2, TResult> 
(this Func<T1, T2, TResult> func, Tuple<T1, T2> parameters) { 
    return func(parameters.Item1, parameters.Item2); 
} 

Y así sucesivamente.

También me gustaría señalar que solo porque no esté en .NET 4.0, eso no significa que no pueda implementar fácilmente su propio tipo Tuple<T1, T2, ...>.

Cuestiones relacionadas