2010-04-30 11 views
10

C'è un modo per fare qualcosa di simile in C#?C'è un modo per fare qualcosa di simile in C#?

public void DoSomething(string parameterA, int parameterB) 
{ 

} 

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

Solo curioso, ha senso? – Alex

+0

In realtà si ...? –

+0

Non riesco davvero ad aggiungere più di quanto sia già stato aggiunto dalle altre risposte, ma sono solo curioso di sapere perché vuoi farlo. Non intendo niente di negativo, è solo quando vedo le persone fare qualcosa fuori dall'ordinario (come questo), non posso fare a meno di chiedermi perché stanno prendendo l'approccio che sono. – senfo

risposta

21

Vicino, ma sfortunatamente solo con oggetto (in modo da ottenere un sacco di boxe/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 per essere il più vicino possibile senza ricorrere al riflesso (o delegare l'invocazione). – stakx

+1

Pensavo avesse detto che il requisito non era quello di cambiare la firma del metodo? Ha anche messo quel requisito in grassetto, lol, quindi segna questo come la risposta! – AaronLS

+0

@AaronLS: Sì, ma questo è l'unico modo, anche se non è perfetto ... –

4

no - questo non è possibile.

8

È possibile richiamare attraverso la riflessione, ma che ti incorrere in un certo overhead:

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

Naturalmente, se è possibile modificare la firma del metodo di prendere una matrice, che sarà il lavoro pure, ma che sembrerà peggio, secondo me.

+2

Non potresti fare semplicemente "Azione m = p.DoSomething; m.DynamicInvoke (parametri); '? –

+0

* gah *, sì, questa è una soluzione migliore! lunga giornata, troppo poco caffè, yaddayaddayadda. Per favore, rispondi alle risposte degli spenditori. –

1

Se sono tutti dello stesso tipo, sì, si può fare qualcosa in questo senso:

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

Altrimenti no, non è possibile espandere i parametri in posizione in quel modo senza utilizzare il riflesso. C# non ha l'equivalente di Ruby splat operator.

2

Si può fare questo (NET 4.0):

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

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

Penso che questa risposta manchi il punto. Questo sarebbe più vicino a ciò che è stato chiesto se la funzione 'DoSomething' accetterebbe la ** tupla ** e la" decomprimi "attraverso i parametri originali. (Ma C# non ha questa caratteristica.) – stakx

+0

Sono d'accordo - questo non risolve il problema (per quanto interessante possa essere). –

+0

@stakx: L'aggiunta di questa "funzionalità" è abbastanza semplice utilizzando un metodo di estensione, però. Dai un'occhiata alla risposta di Henrik. –

10

Non c'è bisogno di utilizzare la riflessione se primo negozio come un delegato, ma richiede una forte dichiarazione del delegato.

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

} 

... e si può dimenticare il tipo di compilazione/controllo di integrità dell'elenco dei parametri. Probabilmente una brutta cosa.

0

"var" rappresenta solo un tipo particolare, è effettivamente una scorciatoia per scrivere un nome di tipo. In quanto sopra non stai specificando alcun tipo. L'unico modo per farlo è quello di fare una classe parametro per rappresentare gli ingressi alla rinfusa ...

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

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

... ma questo è solo andare a essere utile in determinate circostanze. È possibile creare più costruttori di parametri per rappresentare diverse disposizioni di parametri, ma la funzione che si sta chiamando richiederà solo UN input: l'oggetto Parameters. Quindi con questo stai davvero minando la capacità di sovraccaricare una funzione.

Quindi, in breve, no. =)

2

si può fare:

public void DoSomething(string parameterA, int parameterB) 
{ 

} 

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

Haha, mi piace: 'var func = (Azione) (...)'. Un fan della disapprovazione, vero? –

+0

@Dan Tao cosa c'è di così divertente? – Andrey

+1

Hai un 'Action', che hai chiamato' func', e lo hai dichiarato con 'var'. Per me è come scrivere 'var float = (double) 2.5;' ... OK, OK, forse non è così divertente. Ma tu * potresti aver * appena scritto 'Action action =() => DoSomething (" someValue ", 5);' –

3

Forse in questo modo è più "pulito":

// 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

Bello, aggiungi anche qualche testo esplicativo :) –

+1

@Filip: ok, come desideri :) – TcKs

+2

Non come commenti !! ;) –

0

Che ne dite di questo.NET 4 (per il gusto della curiosità)

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

Ispirato dalla risposta di 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

Stavo per suggerire la stessa cosa. Tranne, penso che abbia più senso eseguire "Esegui" un metodo di estensione su "Azione", vero? Quindi: '((Action ) DoSomething) .Execute (parametri);' È conveniente che il tuo non richieda il cast di 'Action', ma è anche piuttosto strano pensare ai parametri che eseguono un'azione. –

+0

+1 Molto bello. – Steven

1

Se non si desidera cambiare la firma del metodo perché non dichiarare un nuovo metodo con la firma appropriata e usalo come proxy Come

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

Si può anche provare alcune delle cose LinFu.Reflection prevede, come tardiva. Con essa è possibile fare qualcosa di simile:

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

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

Per questo è necessario che il metodo DoSomething è all'interno di un oggetto.

14

Non oggi, no. Al momento stiamo prototipando esattamente questa caratteristica per una possibile futura versione ipotetica di C#.

Se è possibile fornire un motivo davvero eccezionale per cui si desidera questa funzionalità, ciò potrebbe essere un punto per riuscire a farlo uscire dalla prototipazione e in una possibile futura release ipotetica. Qual è il tuo fantastico scenario che motiva questa funzione?

(Ricordate, le speculazioni di Eric su possibili future versioni ipotetiche di C# sono per scopi di intrattenimento e non devono essere interpretate come promesse che mai ci sarà un tale rilascio o che avrà un particolare set di funzionalità.)

+1

+1 per la fine :) – RCIX

+0

I secondo che up-voto lol. –

3

Mi piace la risposta di Henrik, tranne che impone una sintassi un po 'strana: i parametri chiamano un metodo su se stessi. Lo farei al contrario. L'unico problema con questo approccio è che ti fa espressamente lanciare un metodo a un delegato.

Comunque, ecco l'idea di base:

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

E così via (per ulteriori T s).

Usage:

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

Action<string, int> action = DoSomething; 

action.Execute(parameters); 

Si può anche facilmente fare questo con un valore di ritorno:

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

E così via.

Vorrei anche far notare che solo perché non si è su .NET 4.0, ciò non significa che non si possa facilmente implementare il proprio tipo Tuple<T1, T2, ...>.

Problemi correlati