Assegnazione del codice a una variabile


124

È possibile creare una variabile e assegnargli una riga di codice, come ad esempio:

ButtonClicked = (MessageBox.Show("Hello, World!"));

... quindi quando uso la variabile, eseguirà la riga di codice.


100
+1 per la rara combinazione di essere nuovo nella programmazione e di porre una buona domanda: capisci cosa vuoi fare e lo spieghi bene, semplicemente non conosci il termine per cui non puoi trovarlo da solo.
Tim S.

10
Il termine che stai cercando è un delegato .
Lasse V. Karlsen,

stackoverflow.com/questions/6187944/… controlla questo, penso che ci sia abbastanza spiegazione di cui avrai bisogno. Asp funziona quasi come winform in quella materia.
CSharpie,

Suona molto simile a blocchi in Objective-c
Brian Tracy,

Risposte:


89

Potresti assegnarlo a un Actionsimile:

var ButtonClicked = new Action(() => MessageBox.Show("hi"));

Quindi chiamalo:

ButtonClicked();

Per completezza (per quanto riguarda i vari commenti) ...

Come affermato da Erik, è possibile eseguire più righe di codice:

var ButtonClicked = new Action(() =>
{
    MessageBox.Show("hi");

    MessageBox.Show("something else");  // something more useful than another popup ;)
});

Come ha affermato Tim, potresti omettere la Actionparola chiave

Action ButtonClicked = () => MessageBox.Show("hi");

Action ButtonClicked = () =>
{
    // multiple lines of code
};

Per rispondere al commento di KRyan, relativo alle parentesi vuote, che rappresenta l'elenco dei parametri che si desidera poter inviare all'Azione (in questo caso, nessuno) .

Se, per esempio, si voleva specificare il messaggio da visualizzare, è possibile aggiungere "messaggio" come parametro (nota che ho cambiato Action per al fine di specificare un singolo parametro stringa) :Action<string>

Action<string> ButtonClicked = (message) => MessageBox.Show(message);

ButtonClicked("hello world!");

10
Action ButtonClicked = () => MessageBox.Show("hi");è equivalente e IMO più bello (aggiungi parentesi se preferisci)
Tim S.

1
È anche possibile che l'azione si risolva in più di una singola riga di codice.
Erik Philips,

2
@CSharpie Non sono sicuro che fare questa ipotesi sia utile per l'OP.
Erik Philips,

2
@CSharpie Perché l'OP non ha potuto utilizzarlo in WinForms?
Vivat Pesci

2
@CSharpie Capisco quello che stai dicendo. Se in realtà lo allega a un Button.Clickevento e non lo memorizza in una variabile a cui è stato assegnato un nome ButtonClicked.
Vivat Pesci

51

Nel tuo caso, vuoi usare a delegate.

Vediamo come funziona un delegato e come possiamo arrivare a una forma più semplice comprendendone il concetto:

// Create a normal function
void OnButtonClick()
{
    MessageBox.Show("Hello World!");
}
// Now we create a delegate called ButtonClick
delegate void ButtonClick();

Vedete, il delegato assume la forma di una normale funzione ma senza alcun argomento (potrebbe prendere qualsiasi quantità di argomenti proprio come qualsiasi altro metodo, ma per semplicità non lo fa).

Ora, usiamo quello che abbiamo; definiremo il delegato così come definiamo qualsiasi altra variabile:

ButtonClick ButtonClicked = new ButtonClick(OnButtonClick);

Fondamentalmente abbiamo creato una nuova variabile chiamata ButtonClicked, che ha un tipo di ButtonClick (che è un delegato) e che, quando utilizzato, eseguirà il metodo nel metodo OnButtonClick ().
Per usarlo chiamiamo semplicemente:ButtonClicked();

Quindi l'intero codice sarebbe:

delegate void ButtonClick();

void OnButtonClick()
{
    MessageBox.Show("Hello World!");
}

void Foo()
{
    ButtonClick ButtonClicked = new ButtonClick(OnButtonClick);
    ButtonClicked(); // Execute the function.
}  

Da qui, possiamo passare alle espressioni lambda e vedere come potrebbero essere utili nella tua situazione:
ci sono molti delegati già definiti dalle librerie .NET, con alcuni come Action, che non accettano alcun parametro e non restituiscono alcun valore. È definito come public delegate void Action();
È sempre possibile utilizzarlo in base alle proprie esigenze anziché alla necessità di definire un nuovo delegato ogni volta. Nel contesto precedente, ad esempio, avresti potuto scrivere

Action ButtonClicked = new Action(OnButtonClick);
ButtonClicked();

che avrebbe fatto lo stesso.
Ora che hai visto diversi modi di usare i delegati, usiamo la nostra prima espressione lambda. Le espressioni lambda sono funzioni anonime; quindi, sono normali funzioni ma senza un nome. Sono di quelle forme:

x => DoSomethingWithX(x);
(x) => DoSomethingWithX(x);
(x,y) => DoSometingWithXY(x,y);
() => Console.WriteLine("I do not have parameters!");

Nel nostro caso, non abbiamo alcun parametro, quindi useremo l'ultima espressione. Possiamo usarlo proprio come la funzione OnButtonClick, ma otteniamo il vantaggio di non avere una funzione denominata. Possiamo invece fare qualcosa del genere:

Action ButtonClicked = new Action( () => MessageBox.Show("Hello World!") );

o anche più facile,

Action ButtonClicked = () => MessageBox.Show("Hello World!");

quindi chiama semplicemente ButtonClicked();Naturalmente puoi anche avere più righe di codice, ma non voglio confonderti di più. Sarebbe così però:

Action ButtonClicked = () => 
{
    MessageBox.Show("Hello World!");
};
ButtonClicked();

Puoi anche giocare, ad esempio, puoi eseguire una funzione come questa:

new Action(() => MessageBox.Show("Hello World!"))();

Ci scusiamo per il lungo post, spero che non sia stato troppo confuso :)

EDIT: ho dimenticato di menzionare che una forma alternativa che, anche se non usata spesso, potrebbe rendere più facile la comprensione delle espressioni lambda:

new Action(delegate() {
    Console.WriteLine("I am parameterless");
})();

Inoltre, usando generics:

// Defines a delegate that has one parameter of type string. You could pass as many parameters as you want.
new Action<string>(delegate(string x) {
    Console.WriteLine(x);
})("I am a string parameter!");

A sua volta potresti usare espressioni lambda, ma non hai bisogno (ma potresti in alcuni casi) di definire il tipo di parametro, ad esempio il codice sopra potrebbe semplicemente essere scritto come:

new Action<string>(x => {
    Console.WriteLine(x);
})("I am a string parameter!");

o:

new Action<string>(x => Console.WriteLine(x))("I am a string parameter!");

EDIT2:
Action<string>è una rappresentazione di public void delegate Action(string obj);
Action<string,string>è una rappresentazione di public void delegate Action(string obj, string obj2);
In generale, Action<T>è una rappresentazione dipublic void delegate Action<T>(T obj);

EDIT3: So che il post è stato qui per un po ', ma penso che sia davvero bello non menzionare: puoi farlo, che è principalmente legato alla tua domanda:

dynamic aFunction = (Func<string, DialogResult>)MessageBox.Show;
aFunction("Hello, world!");

o semplicemente:

Func<string, DialogResult> aFunction = MessageBox.Show;
aFunction("Hello, world!");

7

La Lazyclasse è progettata specificamente per rappresentare un valore che non verrà calcolato fino a quando non lo chiedi. Lo costruisci fornendo un metodo che definisce come dovrebbe essere costruito, ma gestirà l'esecuzione di quel metodo non più di una volta (anche di fronte a più thread che richiedono il valore) e semplicemente restituendo il valore già costruito per eventuali richieste aggiuntive:

var foo = new Lazy<DialogResult>(()=>MessageBox.Show("Hello, World!"));

var result = foo.Value;

Ricorda che Lazydovrebbe essere usato per valori che richiedono molta potenza di elaborazione e che non dovresti usarli per l'interazione (perché la semantica di .Valueè che restituisce un valore, simile a una proprietà, non un'azione (interattiva)). Un delegato dovrebbe invece essere utilizzato per tali azioni.
Abel,

1
@Abel No, non è per i valori che richiedono molta potenza di elaborazione, è per qualsiasi valore di cui si desidera differire l'inizializzazione fino a quando non viene richiesto, senza mai inizializzare quel valore più di una volta. Qui Value viene utilizzato il valore di ; è il DialogResultricevuto da mostrare la finestra di messaggio. La differenza principale tra questa soluzione e l'utilizzo di un delegato è se il valore deve essere ricalcolato ogni volta che viene richiesto o meno. La mia interpretazione dei requisiti era che questo inizializza concettualmente un valore, non un'operazione da ripetere.
Servito il

Lazypuò essere facilmente utilizzato in modo errato. Ha un overhead di se stesso, usandolo "solo" per rinviare un piccolo compito invocherà più overhead di quanto guadagni. Mostrare messagebox da una proprietà è (imo) una cattiva pratica in generale, indipendentemente da Lazy. A proposito, da MSDN, cito: "Usa l'inizializzazione lazy per rinviare la creazione di un oggetto grande o ad alta intensità di risorse" . Non puoi essere d'accordo, ma era quello per cui era stato progettato originariamente.
Abel,

1
@Abel Le prestazioni generali Lazyin un contesto come questo sono certamente trascurabili; impallidirà rispetto al tempo trascorso in attesa che un umano faccia clic su una finestra di messaggio. Dipende principalmente dai reali requisiti dell'applicazione sottostante; la vaghezza della domanda rende impossibile una risposta obiettivamente corretta. Questa è un'interpretazione della domanda. Quanto a fare un sacco di lavoro in un getter di proprietà male; apparentemente sei fondamentalmente contrario a tutto il design di Lazy. Sei il benvenuto a quell'opinione.
Servito il

Scusa, devi avermi frainteso. Certamente, con MessageBox l'overhead è trascurabile (semplicemente non userei l'interfaccia utente all'interno di una proprietà). Intendevo piccoli compiti in generale (come il differimento 2 + 3 * 4 / i), in cui il sovraccarico della creazione della chiusura è maggiore del calcolo stesso. E penso di abbracciarmi completamente Lazy, infatti lo usiamo molto in F # (poco meno in C #) e abbiamo imparato a fondo che devi stare attento con esso, esp. nel rispetto delle prestazioni.
Abel,

4

Il modo in cui sto leggendo la tua domanda, questo è nel contesto dei controlli della GUI?

Se questo è in WPF, dai un'occhiata al modo "giusto" per gestire i comandi dai controlli: http://msdn.microsoft.com/en-us/library/ms752308(v=vs.110).aspx

... ma può essere doloroso e eccessivo. Per un caso generale più semplice, potresti cercare un gestore di eventi, come:

myButton.Click += (o, e) => MessageBox.Show("Hello, World!");

Tale gestore di eventi può essere gestito in vari modi. L'esempio sopra usa una funzione anonima, ma puoi anche fare:

Action<object, RoutedEventArgs> sayHello = (o, e) => MessageBox.Show("Hello, World");
myButton.Click += new RoutedEventHandler(sayHello);

... proprio come stavi chiedendo, con una funzione (o qui, "Azione", poiché restituisce il vuoto) assegnata come variabile.


1

È possibile assegnare il codice C # a una variabile, compilarlo in fase di esecuzione ed eseguire il codice:

  • Scrivi il tuo codice:

    // Assign C# code to the code variable.
    string code = @"
    using System;
    
    namespace First
    {
        public class Program
        {
            public static void Main()
            {
                " +
                "Console.WriteLine(\"Hello, world!\");"
                + @"
            }
        }
    }
    ";
  • Creare il provider e i parametri del compilatore:

    CSharpCodeProvider provider = new CSharpCodeProvider();
    CompilerParameters parameters = new CompilerParameters();
  • Definire i parametri del compilatore:

    // Reference to System.Drawing library
    parameters.ReferencedAssemblies.Add("System.Drawing.dll");
    // True - memory generation, false - external file generation
    parameters.GenerateInMemory = true;
    // True - exe file generation, false - dll file generation
    parameters.GenerateExecutable = true;
  • Compilare l'assemblaggio:

    CompilerResults results = provider.CompileAssemblyFromSource(parameters, code);
  • Verifica errori:

    if (results.Errors.HasErrors)
    {
            StringBuilder sb = new StringBuilder();
    
            foreach (CompilerError error in results.Errors)
            {
                    sb.AppendLine(String.Format("Error ({0}): {1}", error.ErrorNumber, error.ErrorText));
            }
    
            throw new InvalidOperationException(sb.ToString());
    }
  • Ottieni assembly, digitare e il metodo principale:

    Assembly assembly = results.CompiledAssembly;
    Type program = assembly.GetType("First.Program");
    MethodInfo main = program.GetMethod("Main");
  • Eseguirlo:

    main.Invoke(null, null);

Riferimento:

http://www.codeproject.com/Tips/715891/Compiling-Csharp-Code-at-Runtime


Non penso che la compilazione di codice dinamico sia stata rilevante per la domanda.
Iravanchi,
Utilizzando il nostro sito, riconosci di aver letto e compreso le nostre Informativa sui cookie e Informativa sulla privacy.
Licensed under cc by-sa 3.0 with attribution required.