Przypisywanie kodu do zmiennej

124

Czy można utworzyć zmienną i przypisać do niej wiersz kodu, taki jak:

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

... więc kiedy używam zmiennej, wykona linię kodu.

user3539891
źródło
100
+1 za rzadkie połączenie bycia nowicjuszem w programowaniu i zadawania dobrego pytania: rozumiesz, co chcesz zrobić i dobrze to wyjaśniasz, po prostu nie znasz na to terminu, więc nie możesz go znaleźć samodzielnie.
Tim S.,
10
Termin, którego szukasz, to delegat .
Lasse V. Karlsen,
stackoverflow.com/questions/6187944/… sprawdź to, myślę, że jest wystarczająco dużo wyjaśnień, których będziesz potrzebować. Asp działa prawie jak winform w tej kwestii.
CSharpie
Brzmi trochę jak bloki w Objective-c
Brian Tracy,

Odpowiedzi:

89

Możesz go przypisać do Actiontakiego:

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

Następnie nazwij to:

ButtonClicked();

Dla kompletności (w odniesieniu do różnych uwag) ...

Jak stwierdził Erik, możesz wykonać wiele wierszy kodu:

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

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

Jak stwierdził Tim, możesz pominąć Actionsłowo kluczowe

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

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

Aby odnieść się do komentarza KRyana, dotyczącego pustych nawiasów, który reprezentuje listę parametrów, które chcesz wysłać do akcji (w tym przypadku żadnych) .

Jeśli, na przykład, chcesz określić komunikat do wyświetlenia, możesz dodać „komunikat” jako parametr (zwróć uwagę, że zmieniłem Action na , aby określić pojedynczy parametr ciągu) :Action<string>

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

ButtonClicked("hello world!");
vivat ryby
źródło
10
Action ButtonClicked = () => MessageBox.Show("hi");jest równoważny i ładniejszy IMO (dodaj parens, jeśli wolisz)
Tim S.,
1
Możliwe jest również, że akcja zostanie rozwiązana do więcej niż jednej linii kodu.
Erik Philips,
2
@CSharpie Nie jestem pewien, czy przyjęcie tego założenia jest pomocne dla OP.
Erik Philips,
2
@CSharpie Dlaczego OP nie mógł tego użyć WinForms?
vivat pisces
2
@CSharpie Rozumiem, co mówisz. Jeśli faktycznie dołącza to do Button.Clickwydarzenia i nie przechowuje go w zmiennej, którą przypadkiem nazwał ButtonClicked.
vivat pisces
51

W twoim przypadku chcesz użyć delegate.

Zobaczmy, jak działa delegat i jak możemy dostać się do łatwiejszej formy, rozumiejąc jego koncepcję:

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

Widzisz, delegat ma postać normalnej funkcji, ale bez żadnych argumentów (może przyjąć dowolną liczbę argumentów, tak jak każda inna metoda, ale dla uproszczenia tak nie jest).

Teraz użyjmy tego, co mamy; zdefiniujemy delegata tak samo, jak definiujemy każdą inną zmienną:

ButtonClick ButtonClicked = new ButtonClick(OnButtonClick);

Zasadniczo stworzyliśmy nową zmienną o nazwie ButtonClicked, która ma typ ButtonClick (który jest delegatem) i która, gdy zostanie użyta, wykona metodę w metodzie OnButtonClick ().
Aby z niego skorzystać, po prostu dzwonimy:ButtonClicked();

Więc cały kod wyglądałby tak:

delegate void ButtonClick();

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

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

Stąd możemy przejść do wyrażeń lambda i zobaczyć, jak mogą być przydatne w twojej sytuacji:
istnieje wiele delegatów już zdefiniowanych przez biblioteki .NET, z niektórymi takimi jak Action, które nie akceptują żadnego parametru i nie zwracają wartości. Jest zdefiniowany w ten public delegate void Action();
sposób, że zawsze możesz go użyć do swoich potrzeb, zamiast potrzeby definiowania za każdym razem nowego delegata. Na przykład w poprzednim kontekście mógłbyś po prostu napisać

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

który zrobiłby to samo.
Teraz, gdy znasz już różne sposoby używania delegatów, użyjmy naszego pierwszego wyrażenia lambda. Wyrażenia lambda to funkcje anonimowe; są to więc normalne funkcje, ale bez nazwy. Są to formy:

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

W naszym przypadku nie mamy żadnych parametrów, więc użyjemy ostatniego wyrażenia. Możemy tego użyć tak samo jak funkcji OnButtonClick, ale mamy tę zaletę, że nie mamy nazwanej funkcji. Zamiast tego możemy zrobić coś takiego:

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

lub nawet łatwiej,

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

następnie po prostu zadzwoń. ButtonClicked();Oczywiście możesz również mieć wiele linii kodu, ale nie chcę cię bardziej mylić. Wyglądałoby to jednak tak:

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

Możesz też pobawić się, na przykład, możesz wykonać taką funkcję:

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

Przepraszamy za długi post, mam nadzieję, że nie był zbyt zagmatwany :)

EDYCJA: Zapomniałem wspomnieć, że alternatywna forma, która, choć rzadko używana, może ułatwić zrozumienie wyrażeń lambda:

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

Ponadto, używając typów ogólnych:

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

Z kolei możesz użyć wyrażeń lambda, ale nie musisz (ale w niektórych przypadkach może) definiować typ parametru, na przykład powyższy kod można po prostu zapisać jako:

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

lub:

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

EDIT2:
Action<string>jest reprezentacją public void delegate Action(string obj);
Action<string,string>jest reprezentacją public void delegate Action(string obj, string obj2);
Ogólnie Action<T>jest reprezentacjąpublic void delegate Action<T>(T obj);

EDIT3: Wiem, że post był tu od jakiegoś czasu, ale myślę, że naprawdę fajnie jest nie wspominać: Możesz to zrobić, co jest głównie związane z twoim pytaniem:

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

lub po prostu:

Func<string, DialogResult> aFunction = MessageBox.Show;
aFunction("Hello, world!");
user3439065
źródło
7

LazyKlasa jest specjalnie zaprojektowany, aby reprezentować wartości, które nie będą obliczane aż o to poprosić. Konstruujesz go, dostarczając metodę, która definiuje, jak powinna być skonstruowana, ale poradzi sobie z wykonaniem tej metody nie więcej niż raz (nawet w obliczu wielu wątków żądających wartości) i po prostu zwraca już skonstruowaną wartość dla wszelkich dodatkowych żądań:

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

var result = foo.Value;
Servy
źródło
Pamiętaj, że Lazypowinno być używane dla wartości, które wymagają dużej mocy obliczeniowej i że nie powinieneś ich używać do interakcji (ponieważ semantyka .Valuejest taka, że ​​zwraca wartość podobną do właściwości, a nie do (interaktywnej) akcji). Zamiast tego do takich działań należy użyć delegata.
Abel
1
@Abel Nie, to nie jest dla wartości, które wymagają dużej mocy obliczeniowej, ale dla każdej wartości, o którą chciałbyś odroczyć inicjalizację, dopóki nie zostanie o nią poproszona, ale nigdy nie inicjalizujesz tej wartości więcej niż raz. Tutaj używana Value jest wartość ; jest to DialogResultotrzymane z wyświetlenia okna komunikatu. Podstawowa różnica między tym rozwiązaniem a użyciem delegata polega na tym, czy wartość powinna być ponownie obliczana za każdym razem, gdy jest żądana, czy nie. Moja interpretacja wymagań była taka, że ​​jest to koncepcyjna inicjalizacja wartości, a nie operacja do powtórzenia.
Servy
Lazymożna łatwo niewłaściwie wykorzystać. Ma na sobie narzut, a użycie go „tylko” do odroczenia małego zadania spowoduje większe obciążenie niż zyskuje. Pokazywanie skrzynek wiadomości z właściwości jest ogólnie złą praktyką (imo), niezależnie od tego Lazy. Przy okazji, z MSDN cytuję: „Użyj leniwej inicjalizacji, aby odroczyć tworzenie dużego lub wymagającego dużej ilości zasobów obiektu” . Możesz się z tym nie zgodzić, ale do tego pierwotnie był przeznaczony.
Abel
1
@Abel Narzut wydajności Lazyw takim kontekście jest z pewnością pomijalny; blednie w porównaniu z czasem spędzonym na czekaniu, aż człowiek kliknie okno wiadomości. Sprowadza się to głównie do rzeczywistych wymagań aplikacji bazowej; niejasność pytania uniemożliwia obiektywnie poprawną odpowiedź. To jest jedna interpretacja pytania. Jeśli chodzi o wykonywanie dużej ilości pracy w celu uzyskania złej własności; najwyraźniej zasadniczo sprzeciwiasz się całemu projektowi Lazy. Zapraszamy do tej opinii.
Servy
Przepraszam, musiałeś mnie źle zrozumieć. Z pewnością MessageBox narzut jest znikomy (po prostu nie użyłbym interfejsu użytkownika wewnątrz nieruchomości). Miałem na myśli ogólnie małe zadania (takie jak odraczanie 2 + 3 * 4 / i), w których narzut związany z utworzeniem zamknięcia jest większy niż same obliczenia. I myślę, że w pełni to rozumiem Lazy, w rzeczywistości używamy go dużo w F # (trochę mniej w C #) i na własnej skórze nauczyliśmy się, że trzeba z tym uważać, szczególnie. w odniesieniu do wydajności.
Abel
4

Sposób, w jaki czytam twoje pytanie, czy to w kontekście elementów sterujących GUI?

Jeśli to jest w WPF, spójrz na „właściwy” sposób obsługi poleceń z formantów: http://msdn.microsoft.com/en-us/library/ms752308(v=vs.110).aspx

... ale to może być bólem i przesadą. Dla prostszego przypadku ogólnego możesz szukać programu obsługi zdarzeń, takiego jak:

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

Ten program obsługi zdarzeń można obsługiwać na różne sposoby. Powyższy przykład używa funkcji anonimowej, ale możesz też zrobić:

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

... tak jak pytałeś, z funkcją (lub tutaj „Action”, ponieważ zwraca void) przypisaną jako zmienna.

Zaccone
źródło
1

Możesz przypisać kod C # do zmiennej, kompilując ją w czasie wykonywania i uruchamiając kod:

  • Wpisz swój kod:

    // Assign C# code to the code variable.
    string code = @"
    using System;
    
    namespace First
    {
        public class Program
        {
            public static void Main()
            {
                " +
                "Console.WriteLine(\"Hello, world!\");"
                + @"
            }
        }
    }
    ";
  • Utwórz dostawcę i parametry kompilatora:

    CSharpCodeProvider provider = new CSharpCodeProvider();
    CompilerParameters parameters = new CompilerParameters();
  • Zdefiniuj parametry kompilatora:

    // 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;
  • Skompiluj montaż:

    CompilerResults results = provider.CompileAssemblyFromSource(parameters, code);
  • Sprawdź błędy:

    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());
    }
  • Uzyskaj montaż, typ i metodę główną:

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

    main.Invoke(null, null);

Odniesienie:

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

Amir Saniyan
źródło
Nie sądzę, aby dynamiczna kompilacja kodu była w ogóle odpowiednia dla zadanego pytania.
Iravanchi