Atribuindo código a uma variável

124

É possível criar uma variável e atribuir uma linha de código a ela, como:

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

... então, quando eu uso a variável, ela executa a linha de código.

user3539891
fonte
100
+1 pela rara combinação de ser novo na codificação e fazer uma boa pergunta: você entende o que deseja fazer e o explica bem, simplesmente não conhece o termo para ele e não pode encontrá-lo sozinho.
Tim S.
10
O termo que você está procurando é um delegado .
Lasse V. Karlsen
stackoverflow.com/questions/6187944/… verifique isso, acho que há explanações suficientes que você precisará. Asp funciona quase como winforms nesse assunto.
CSharpie
Soa muito parecido com blocos no Objective-c #
Brian Tracy

Respostas:

89

Você pode atribuí-lo a um Actionassim:

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

Então chame-o:

ButtonClicked();

Por uma questão de integridade (em relação aos vários comentários) ...

Como Erik afirmou, você pode executar várias linhas de código:

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

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

Como Tim afirmou, você pode omitir a Actionpalavra - chave

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

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

Para abordar o comentário do KRyan, sobre os parênteses vazios, que representa a lista de parâmetros que você deseja enviar para a Ação (neste caso, nenhum) .

Se, por exemplo, você quiser especificar a mensagem a ser exibida, poderá adicionar "message" como parâmetro (observe que mudei Action para para especificar um único parâmetro de string) :Action<string>

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

ButtonClicked("hello world!");
vivat pisces
fonte
10
Action ButtonClicked = () => MessageBox.Show("hi");é equivalente e IMO mais agradável (adicionar parênteses se você preferir)
Tim S.
1
Também é possível que a ação resolva para mais de uma única linha de código.
Erik Philips
2
@ CSharpie Não tenho certeza de que fazer essa suposição seja útil para o OP.
Erik Philips
2
@CSharpie Por que o OP não pôde usar isso WinForms?
vivat peixes
2
@ CSharpie eu vejo o que você está dizendo. Se ele está realmente anexando isso a um Button.Clickevento, e não o armazenando em uma variável que ele nomeou ButtonClicked.
vivat peixes
51

No seu caso, você deseja usar a delegate.

Vamos ver como um delegado funciona e como podemos chegar a uma forma mais fácil, entendendo seu conceito:

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

Veja bem, o delegado assume a forma de uma função normal, mas sem argumentos (poderia levar qualquer quantidade de argumentos como qualquer outro método, mas por uma questão de simplicidade, não).

Agora, vamos usar o que temos; definiremos o delegado da mesma maneira que definimos qualquer outra variável:

ButtonClick ButtonClicked = new ButtonClick(OnButtonClick);

Basicamente, criamos uma nova variável chamada ButtonClicked, que possui um tipo de ButtonClick (que é um delegado) e que, quando usado, executará o método no método OnButtonClick ().
Para usá-lo, simplesmente chamamos:ButtonClicked();

Portanto, o código inteiro seria:

delegate void ButtonClick();

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

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

A partir daqui, podemos passar para expressões lambda e ver como elas podem ser úteis na sua situação:
Existem muitos delegados já definidos pelas bibliotecas .NET, com alguns como Action, que não aceitam nenhum parâmetro e não retornam um valor. É definido como public delegate void Action();
Você sempre pode usá-lo de acordo com suas necessidades, em vez da necessidade de definir um novo delegado sempre. No contexto anterior, por exemplo, você poderia escrever

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

o que teria feito o mesmo.
Agora que você viu maneiras diferentes de usar delegados, vamos usar nossa primeira expressão lambda. Expressões lambda são funções anônimas; portanto, são funções normais, mas sem nome. Eles são dessas formas:

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

No nosso caso, como não temos parâmetros, usaremos a última expressão. Podemos usar isso como a função OnButtonClick, mas temos a vantagem de não ter uma função nomeada. Em vez disso, podemos fazer algo assim:

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

ou ainda mais fácil,

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

então simplesmente chame ButtonClicked();Claro que você também pode ter várias linhas de código, mas não quero confundir mais. Seria assim:

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

Você também pode brincar, por exemplo, pode executar uma função como esta:

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

Desculpe pelo longo post, espero que não tenha sido muito confuso :)

EDIT: Esqueci de mencionar que uma forma alternativa que, embora não seja usada com frequência, poderia facilitar a compreensão das expressões lambda:

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

Além disso, 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!");

Por sua vez, você pode usar expressões lambda, mas não precisa (mas pode em alguns casos) definir o tipo do parâmetro, por exemplo, o código acima pode ser simplesmente escrito como:

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

ou:

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

EDIT2:
Action<string>é uma representação de public void delegate Action(string obj);
Action<string,string>é uma representação de public void delegate Action(string obj, string obj2);
Em geral, Action<T>é uma representação depublic void delegate Action<T>(T obj);

EDIT3: Eu sei que o post está aqui há um tempo, mas acho que é muito legal não mencionar: você pode fazer isso, principalmente relacionado à sua pergunta:

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

ou simplesmente:

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

A Lazyclasse foi projetada especificamente para representar um valor que não será calculado até que você solicite. Você o constrói fornecendo um método que define como ele deve ser construído, mas ele manipula a execução desse método não mais de uma vez (mesmo diante de vários encadeamentos que solicitam o valor) e simplesmente retorna o valor já construído para quaisquer solicitações adicionais:

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

var result = foo.Value;
Servy
fonte
Lembre-se de que Lazydeve ser usado para valores que exijam muito poder de processamento e que você não deve usá-los para interação (porque a semântica .Valueé que ele retorna um valor, semelhante a uma propriedade, e não uma ação (interativa)). Um delegado deve ser usado para essas ações.
Abel
1
@Abel Não, não se trata de valores que exigem muito poder de processamento, mas de qualquer valor que você queira adiar a inicialização até que seja solicitada, sem nunca inicializar esse valor mais de uma vez. Aqui o valor de Value é usado; é o DialogResultrecebido de mostrar a caixa de mensagem. A principal diferença entre essa solução e o uso de um representante é se o valor deve ser recalculado sempre que solicitado ou não. Minha interpretação dos requisitos foi que isso está inicializando conceitualmente um valor, não uma operação a ser repetida.
Servy
Lazypode ser facilmente usado incorretamente. Ele tem sobrecarga de si mesmo, usá-lo "apenas" para adiar uma pequena tarefa invocará mais sobrecarga do que ganha. Mostrar caixas de mensagens de uma propriedade é (imo) prática recomendada em geral, independentemente de Lazy. Btw, do MSDN, cito: "Use a inicialização lenta para adiar a criação de um objeto grande ou com muitos recursos" . Você pode discordar disso, mas foi para isso que foi projetado originalmente.
Abel
1
@Abel A sobrecarga de desempenho Lazyem um contexto como esse é certamente insignificante; ficará pálido em comparação com o tempo gasto esperando que um humano clique em uma caixa de mensagem. Isso se resume principalmente aos requisitos reais do aplicativo subjacente; a imprecisão da pergunta torna impossível uma resposta objetivamente correta. Esta é uma interpretação da questão. Quanto a fazer muito trabalho em um getter de propriedade ser ruim; aparentemente você é fundamentalmente contrário a todo o design de Lazy. Você é bem-vindo a essa opinião.
Servy
Desculpe, você deve ter me entendido mal. Certamente, com MessageBox a sobrecarga é insignificante (eu não usaria a interface do usuário dentro de uma propriedade). Eu quis dizer pequenas tarefas em geral (como adiar 2 + 3 * 4 / i), em que a sobrecarga de criação do fechamento é maior que o próprio cálculo. E acho que aceito totalmente Lazy, na verdade, usamos muito em F # (um pouco menos em C #) e aprendemos da maneira mais difícil que você precisa para ter cuidado com isso, esp. em relação ao desempenho.
Abel
4

Do jeito que estou lendo sua pergunta, isso é no contexto dos controles da GUI?

Se isso estiver no WPF, consulte a maneira "correta" de manipular comandos de controles: http://msdn.microsoft.com/en-us/library/ms752308(v=vs.110).aspx

... mas isso pode ser uma dor e um exagero. Para um caso geral mais simples, você pode estar procurando um manipulador de eventos, como:

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

Esse manipulador de eventos pode ser tratado de várias maneiras. O exemplo acima usa uma função anônima, mas você também pode:

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

... exatamente como você estava perguntando, com uma função (ou aqui, "Ação", pois retorna nula) atribuída como uma variável.

Zaccone
fonte
1

Você pode atribuir código C # a uma variável, compilando-o em tempo de execução e executando o código:

  • Escreva seu 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!\");"
                + @"
            }
        }
    }
    ";
  • Crie o provedor e os parâmetros do compilador:

    CSharpCodeProvider provider = new CSharpCodeProvider();
    CompilerParameters parameters = new CompilerParameters();
  • Defina parâmetros do 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 montagem:

    CompilerResults results = provider.CompileAssemblyFromSource(parameters, code);
  • Verifique os erros:

    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());
    }
  • Obtenha montagem, tipo e o método Main:

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

    main.Invoke(null, null);

Referência:

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

Amir Saniyan
fonte
Não acho que a compilação dinâmica de código seja relevante para a pergunta.
Iravanchi