Присвоєння коду змінній


124

Чи можливо зробити змінну та призначити їй рядок коду, наприклад:

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

... тому коли я використовую змінну, вона виконає рядок коду.


100
+1 за рідкісну комбінацію, яка є новою для кодування та задає гарне запитання: ти розумієш, що ти хочеш зробити, і добре це пояснюєш, ти просто не знаєш термін для цього, тому не можеш знайти його самостійно.
Тім С.

10
Термін, який ви шукаєте, - це делегат .
Лассе В. Карлсен

stackoverflow.com/questions/6187944/… перевірте це, я думаю, що вам буде достатньо пояснень. Asp працює майже як winforms в цьому питанні.
CSharpie

Дуже схоже на блоки в Objective-c
Брайан Трейсі

Відповіді:


89

Ви можете призначити його Actionтак:

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

Тоді зателефонуйте:

ButtonClicked();

Для повноти (стосовно різних коментарів) ...

Як заявив Ерік, ви можете виконати кілька рядків коду:

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

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

Як заявив Тім, ви можете пропустити Actionключове слово

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

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

Звернутися до коментаря KRyan щодо порожніх дужок, що представляє список параметрів, які ви хочете мати можливість надіслати до дії (у цьому випадку жодного) .

Якщо, наприклад, ви хочете , щоб вказати повідомлення , щоб показати, можна додати «повідомлення» в якості параметра (зверніть увагу , що я змінив , Action щоб для того , щоб вказати один параметр рядка) :Action<string>

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

ButtonClicked("hello world!");

10
Action ButtonClicked = () => MessageBox.Show("hi");еквівалентний і IMO приємніше (додайте паролі, якщо вам більше зручно)
Тім С.

1
Можливо також, що дія буде вирішена на більш ніж один рядок коду.
Ерік Філіпс

2
@CSharpie Я не впевнений, що зробити це припущення корисним для ОП.
Ерік Філіпс

2
@CSharpie Чому ОП не могло це використати WinForms?
vivat rices

2
@CSharpie Я бачу, що ти кажеш. Якщо він насправді приєднує це до Button.Clickподії, а не зберігає його в змінній, яку він випадково назвав ButtonClicked.
vivat rices

51

У вашому випадку ви хочете використовувати delegate.

Подивимось, як працює делегат і як ми можемо дійти до більш легкої форми, розуміючи його концепцію:

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

Розумієте, делегат має форму звичайної функції, але без жодних аргументів (це може приймати будь-яку кількість аргументів, як і будь-який інший метод, але для простоти це не так).

Тепер давайте скористаємося тим, що маємо; ми визначимо делегата так само, як ми визначимо будь-яку іншу змінну:

ButtonClick ButtonClicked = new ButtonClick(OnButtonClick);

В основному ми створили нову змінну під назвою ButtonClicked, яка має тип ButtonClick (який є делегатом) і яка при використанні виконуватиме метод методу OnButtonClick ().
Для його використання ми просто закликаємо:ButtonClicked();

Отже, весь код буде таким:

delegate void ButtonClick();

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

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

Звідси ми можемо перейти до лямбда-виразів і побачити, як вони можуть бути корисними у вашій ситуації:
Є багато делегатів, які вже визначені бібліотеками .NET, з деякими на зразок Action, які не приймають жодного параметра і не повертають значення. Він визначається так, як public delegate void Action();
Ви завжди можете використовувати його для своїх потреб замість потреби кожного разу визначати нового делегата. Наприклад, у попередньому контексті ви могли щойно писати

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

який би зробив те саме.
Тепер, коли ви побачили різні способи використання делегатів, давайте скористаємося нашим першим лямбда-виразом. Лямбда-вирази - це анонімні функції; Таким чином, вони є нормальними функціями, але без імені. Вони бувають таких форм:

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

У нашому випадку у нас немає жодних параметрів, тому ми будемо використовувати останній вираз. Ми можемо використовувати це як функцію OnButtonClick, але ми отримуємо перевагу у відсутності іменованої функції. Замість цього ми можемо зробити щось подібне:

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

або навіть простіше,

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

тоді просто зателефонуйте ButtonClicked();Звичайно, ви також можете мати багато рядків коду, але я більше не хочу вас плутати. Це виглядатиме так:

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

Ви також можете пограти, наприклад, можете виконати таку функцію:

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

Вибачте за довгий пост, сподіваюся, що це не надто заплутано :)

EDIT: Я забув згадати, що альтернативна форма, яка, хоч і не часто використовується, може полегшити розуміння лямбда-виразів:

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

Також, використовуючи дженерики:

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

У свою чергу ви можете використовувати лямбда-вирази, але вам не потрібно (але в деяких випадках) визначати тип параметра, наприклад, код вище може бути просто записаний як:

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

або:

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

EDIT2:
Action<string>являє собою представлення public void delegate Action(string obj);
Action<string,string>є представлення public void delegate Action(string obj, string obj2);
загалом, Action<T>є представленняpublic void delegate Action<T>(T obj);

EDIT3: Я знаю, що публікація тут була деякий час, але я думаю, що це дуже здорово, щоб не згадувати: Ви можете це зробити, що здебільшого пов'язане з вашим питанням:

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

або просто:

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

7

LazyКлас розроблений спеціально для подання значення , яке не буде обчислюватися , поки ви не попросите його. Ви конструюєте його, надаючи метод, який визначає, як він повинен бути побудований, але він буде обробляти виконання цього методу не більше одного разу (навіть перед численними потоками, що вимагають значення) і просто повертає вже побудоване значення для будь-яких додаткових запитів:

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

var result = foo.Value;

Пам'ятайте, що його Lazyслід використовувати для значень, які потребують великої потужності обробки, і ви не повинні використовувати їх для взаємодії (адже семантика цього .Valueполягає в тому, що він повертає значення, подібне до властивості, а не (інтерактивна) дія). Для таких дій замість цього повинен бути використаний делегат.
Авель

1
@Abel Ні, це не для значень, які потребують великої потужності обробки, це будь-яке значення, на яке ви хочете відкласти ініціалізацію до тих пір, поки не буде запропоновано, але не ініціалізуючи це значення не один раз. При цьому значення Value буде використано; це DialogResultотримане від показу вікно повідомлення. Основна відмінність цього рішення від використання делегата полягає в тому, чи варто перераховувати значення кожного разу, коли воно запитується чи ні. Моє тлумачення вимог полягало в тому, що це концептуально ініціалізує значення, а не операцію, яку слід повторити.
Сервіс

Lazyлегко можуть бути неправильно використані. Він має накладні витрати, використовуючи його "просто" для відкладання невеликого завдання, викличе більше накладних витрат, ніж отримує. Відображення скриньки повідомлень із властивості - це (іммо) загальна практика, незалежно від Lazy. Btw, з MSDN, я цитую: "Використовуйте ліниву ініціалізацію для відстрочення створення великого або ресурсомісткого об'єкта" . Ви можете не погодитися з цим, але саме так було розраховано спочатку.
Авель

1
@Abel Виконання ефективності Lazyв такому контексті, безумовно, незначне; вона зблідне в порівнянні з часом, витраченим на очікування людини, щоб натиснути на поле повідомлень. В основному це зводиться до реальних вимог базової програми; невизначеність питання робить неможливим об'єктивно правильну відповідь. Це одне тлумачення питання. Що стосується того, що робити велику роботу в тому, що власник нерухомості поганий; мабуть, ви принципово проти всієї конструкції Lazy. Вас вітає ця думка.
Сервіс

Вибачте, ви, мабуть, зрозуміли мене неправильно. Звичайно, MessageBox накладні витрати незначні (я просто не використовував би інтерфейс користувача у власність). Я мав на увазі невеликі завдання в цілому (наприклад, відстрочка 2 + 3 * 4 / i), де витрати на створення закриття більше, ніж сам розрахунок. І я думаю, що я цілком сприймаю Lazy, адже ми багато використовуємо його в F # (трохи менше в C #), і ми навчилися важкому шляху, що вам потрібно бути обережним, особливо. стосовно продуктивності.
Авель

4

Як я читаю ваше запитання, це в контексті управління графічним інтерфейсом?

Якщо це в WPF, погляньте на "правильний" спосіб обробки команд з елементів управління: http://msdn.microsoft.com/en-us/library/ms752308(v=vs.110).aspx

... але це може бути болем і надмірністю. Для більш простого загального випадку ви можете шукати обробника подій, наприклад:

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

З цим обробником подій можна обробляти різними способами. У наведеному вище прикладі використовується анонімна функція, але ви також можете зробити:

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

... так само, як ви запитували, функція (або тут "Дія", оскільки вона повертає недійсність), призначена як змінна.


1

Ви можете призначити C # коду змінній, компілюючи її під час виконання та запустити код:

  • Напишіть свій код:

    // Assign C# code to the code variable.
    string code = @"
    using System;
    
    namespace First
    {
        public class Program
        {
            public static void Main()
            {
                " +
                "Console.WriteLine(\"Hello, world!\");"
                + @"
            }
        }
    }
    ";
  • Створіть постачальника та параметри компілятора:

    CSharpCodeProvider provider = new CSharpCodeProvider();
    CompilerParameters parameters = new CompilerParameters();
  • Визначте параметри компілятора:

    // 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;
  • Збірка:

    CompilerResults results = provider.CompileAssemblyFromSource(parameters, code);
  • Перевірте помилки:

    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());
    }
  • Отримайте збірку, тип та основний метод:

    Assembly assembly = results.CompiledAssembly;
    Type program = assembly.GetType("First.Program");
    MethodInfo main = program.GetMethod("Main");
  • Виконати:

    main.Invoke(null, null);

Довідка:

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


Я не думаю, що динамічна компіляція коду взагалі не стосувалася поставленого питання.
Іраванчі
Використовуючи наш веб-сайт, ви визнаєте, що прочитали та зрозуміли наші Політику щодо файлів cookie та Політику конфіденційності.
Licensed under cc by-sa 3.0 with attribution required.