Asignación de código a una variable

124

¿Es posible hacer una variable y asignarle una línea de código, como:

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

... entonces cuando uso la variable, ejecutará la línea de código.

user3539891
fuente
100
+1 por la rara combinación de ser nuevo en la codificación y hacer una buena pregunta: entiendes lo que quieres hacer y lo explicas bien, simplemente no conoces el término para que no puedas encontrarlo por tu cuenta.
Tim S.
10
El término que está buscando es delegado .
Lasse V. Karlsen
stackoverflow.com/questions/6187944/… mira esto, creo que hay suficiente explicación que necesitarás. Asp funciona casi como winforms en ese asunto.
CSharpie
Suena como bloques en Objective-c
Brian Tracy

Respuestas:

89

Podrías asignarlo a algo Actionasí:

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

Entonces llámalo:

ButtonClicked();

Para completar (en lo que respecta a los diversos comentarios) ...

Como dijo Erik, podría ejecutar múltiples líneas de código:

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

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

Como dijo Tim, podría omitir la Actionpalabra clave

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

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

Para abordar el comentario de KRyan, con respecto a los paréntesis vacíos, que representa la lista de parámetros que desea poder enviar a la Acción (en este caso, ninguno) .

Si, por ejemplo, desea especificar el mensaje a mostrar, puede agregar "mensaje" como parámetro (tenga en cuenta que cambié Action a para especificar un único parámetro de cadena) :Action<string>

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

ButtonClicked("hello world!");
vivat piscis
fuente
10
Action ButtonClicked = () => MessageBox.Show("hi");es equivalente e IMO más agradable (agregue parens si lo prefiere)
Tim S.
1
También es posible que la acción se resuelva en más de una sola línea de código.
Erik Philips
2
@CSharpie No estoy seguro de que hacer esa suposición sea útil para el OP.
Erik Philips
2
@CSharpie ¿Por qué el OP no pudo usar esto WinForms?
vivat pisces
2
@CSharpie Ya veo lo que estás diciendo. Si en realidad está adjuntando esto a un Button.Clickevento, y no lo almacena en una variable que pasó a nombrar ButtonClicked.
vivat pisces
51

En su caso, desea usar a delegate.

Veamos cómo funciona un delegado y cómo podemos llegar a una forma más fácil al comprender su concepto:

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

Verá, el delegado toma la forma de una función normal pero sin ningún argumento (podría tomar cualquier cantidad de argumentos como cualquier otro método, pero en aras de la simplicidad, no lo hace).

Ahora, usemos lo que tenemos; definiremos el delegado tal como definimos cualquier otra variable:

ButtonClick ButtonClicked = new ButtonClick(OnButtonClick);

Básicamente, creamos una nueva variable llamada ButtonClicked, que tiene un tipo de ButtonClick (que es un delegado) y que, cuando se usa, ejecutará el método en el método OnButtonClick ().
Para usarlo simplemente llamamos:ButtonClicked();

Entonces todo el código sería:

delegate void ButtonClick();

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

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

Desde aquí, podemos pasar a las expresiones lambda y ver cómo podrían ser útiles en su situación:
hay muchos delegados ya definidos por las bibliotecas .NET, con algunos como Acción, que no aceptan ningún parámetro y no devuelven un valor. Se define como public delegate void Action();
Siempre puede usarlo según sus necesidades en lugar de la necesidad de definir un nuevo delegado cada vez. En el contexto anterior, por ejemplo, podría haber escrito

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

que hubiera hecho lo mismo.
Ahora que vio diferentes formas de usar delegados, usemos nuestra primera expresión lambda. Las expresiones lambda son funciones anónimas; entonces, son funciones normales pero sin nombre. Son de esas formas:

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

En nuestro caso, no tenemos ningún parámetro, por lo que utilizaremos la última expresión. Podemos usar esto como la función OnButtonClick, pero tenemos la ventaja de no tener una función con nombre. En cambio, podemos hacer algo como esto:

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

o incluso más fácil,

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

entonces simplemente llame ButtonClicked();Por supuesto, también puede tener varias líneas de código, pero no quiero confundirlo más. Sin embargo, se vería así:

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

También podría jugar, por ejemplo, puede ejecutar una función como esta:

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

Perdón por la larga publicación, espero que no haya sido demasiado confusa :)

EDITAR: Olvidé mencionar que una forma alternativa que, aunque no se usa con frecuencia, podría hacer que las expresiones lambda sean más fáciles de entender:

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

Además, usando genéricos:

// 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 su vez, podría usar expresiones lambda, pero no necesita (pero podría en algunos casos) definir el tipo de parámetro, por ejemplo, el código anterior podría simplemente escribirse como:

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>es una representación de public void delegate Action(string obj);
Action<string,string>es una representación de public void delegate Action(string obj, string obj2);
En general, Action<T>es una representación depublic void delegate Action<T>(T obj);

EDIT3: Sé que la publicación ha estado aquí por un tiempo, pero creo que es genial no mencionarlo: puedes hacer esto, que está relacionado principalmente con tu pregunta:

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

o simplemente:

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

La Lazyclase está diseñada específicamente para representar un valor que no se calculará hasta que lo solicite. Lo construye proporcionando un método que define cómo debe construirse, pero se encargará de ejecutar ese método no más de una vez (incluso frente a múltiples subprocesos que solicitan el valor) y simplemente devolver el valor ya construido para cualquier solicitud adicional:

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

var result = foo.Value;
Servy
fuente
Recuerde que Lazydebe usarse para valores que requieren mucha potencia de procesamiento, y que no debe usarlos para la interacción (porque la semántica de .Valuees que devuelve un valor, similar a una propiedad, no una acción (interactiva)). En su lugar, se debe usar un delegado para tales acciones.
Abel
1
@Abel No, es no para los valores que requieren una gran cantidad de potencia de procesamiento, que es para cualquier valor que le gustaría aplazar la inicialización hasta que se pidió, aunque no siempre la inicialización de ese valor más de una vez. Aquí Value se usa el valor de ; es el DialogResultrecibido al mostrar el cuadro de mensaje. La principal diferencia entre esta solución y el uso de un delegado es si el valor se debe volver a calcular cada vez que se solicita o no. Mi interpretación de los requisitos fue que esto inicializa conceptualmente un valor, no una operación que se repita.
Servy
Lazypuede ser usado incorrectamente fácilmente. Tiene sobrecarga de sí mismo, usarlo "solo" para diferir una tarea pequeña invocará más sobrecarga de la que gana. Mostrar cuadros de mensaje de una propiedad es (imo) una mala práctica en general, independientemente de Lazy. Por cierto, de MSDN, cito: "Use la inicialización diferida para diferir la creación de un objeto grande o que requiera muchos recursos" . Puede estar en desacuerdo con eso, pero para eso fue diseñado originalmente.
Abel
1
@Abel La sobrecarga de rendimiento Lazyen un contexto como este es ciertamente insignificante; palidecerá en comparación con el tiempo dedicado a esperar que un humano haga clic en un cuadro de mensaje. Principalmente se reduce a los requisitos reales de la aplicación subyacente; La vaguedad de la pregunta hace imposible una respuesta objetivamente correcta. Esta es una interpretación de la pregunta. En cuanto a que hacer mucho trabajo en un captador de propiedades es malo; aparentemente te opones fundamentalmente a todo el diseño de Lazy. De nada a esa opinión.
Servy
Lo siento, debes haberme entendido mal. Ciertamente, con MessageBox la sobrecarga es insignificante (simplemente no usaría la interfaz de usuario dentro de una propiedad). Me refería a pequeñas tareas en general (como diferir 2 + 3 * 4 / i), donde la sobrecarga de crear el cierre es mayor que el cálculo en sí. Y creo que lo abrazo por completo Lazy, de hecho lo usamos mucho en F # (poco menos en C #) y hemos aprendido de la manera difícil que hay que tener cuidado con eso, especialmente. en respeto con el rendimiento.
Abel
4

La forma en que estoy leyendo tu pregunta, ¿es en el contexto de los controles de la GUI?

Si esto está en WPF, eche un vistazo a la forma "correcta" de manejar los comandos de los controles: http://msdn.microsoft.com/en-us/library/ms752308(v=vs.110).aspx

... pero eso puede ser un dolor y una exageración. Para un caso general más simple, es posible que esté buscando un controlador de eventos, como:

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

Ese controlador de eventos se puede manejar de varias maneras. El ejemplo anterior usa una función anónima, pero también podría hacer:

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

... tal como estaba preguntando, con una función (o aquí, "Acción", ya que devuelve vacío) asignada como una variable.

Zaccone
fuente
1

Puede asignar el código C # a una variable, compilarlo en tiempo de ejecución y ejecutar el código:

  • Escribe tu código:

    // Assign C# code to the code variable.
    string code = @"
    using System;
    
    namespace First
    {
        public class Program
        {
            public static void Main()
            {
                " +
                "Console.WriteLine(\"Hello, world!\");"
                + @"
            }
        }
    }
    ";
  • Cree el proveedor y los parámetros del compilador:

    CSharpCodeProvider provider = new CSharpCodeProvider();
    CompilerParameters parameters = new CompilerParameters();
  • Defina los parámetros del compilador:

    // 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;
  • Compilar ensamblaje:

    CompilerResults results = provider.CompileAssemblyFromSource(parameters, code);
  • Verificar errores:

    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());
    }
  • Obtenga el ensamblaje, el tipo y el método principal:

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

    main.Invoke(null, null);

Referencia:

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

Amir Saniyan
fuente
No creo que la compilación de código dinámico haya sido relevante para la pregunta formulada.
Iravanchi