Присвоение кода переменной

124

Можно ли создать переменную и присвоить ей строку кода, например:

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

... поэтому, когда я использую переменную, она выполнит строку кода.

user3539891
источник
100
+1 за редкое сочетание того, что вы новичок в программировании и задаете хороший вопрос: вы понимаете, что хотите сделать, и хорошо это объясняете, вы просто не знаете термина для этого, поэтому не можете найти его самостоятельно.
Тим С.
10
Термин, который вы ищете, - это делегат .
Лассе В. Карлсен
stackoverflow.com/questions/6187944/… проверьте это, я думаю, вам понадобится достаточно объяснений. В этом вопросе Asp работает почти как winform.
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
};

Чтобы обратиться к комментарию Крайана относительно пустых круглых скобок, который представляет список параметров, которые вы хотите отправить в действие (в данном случае, ни одного) .

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

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

ButtonClicked("hello world!");
виват рыбы
источник
10
Action ButtonClicked = () => MessageBox.Show("hi");эквивалентен и IMO лучше (добавьте скобки, если хотите)
Тим С.
1
Также возможно, что действие будет разрешено более чем в одну строку кода.
Эрик Филипс
2
@CSharpie Я не уверен, что это предположение полезно для OP.
Эрик Филипс
2
@CSharpie Почему OP не мог использовать это WinForms?
vivat pisces
2
@CSharpie, я понимаю, о чем вы говорите. Если он действительно прикрепляет это к Button.Clickсобытию, а не сохраняет его в переменной, которую он случайно назвал ButtonClicked.
vivat pisces
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!"))();

Извините за длинный пост, надеюсь, он не слишком запутал :)

РЕДАКТИРОВАТЬ: я забыл упомянуть, что альтернативная форма, которая, хотя и не часто используется, может упростить понимание лямбда-выражений:

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!");
user3439065
источник
7

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

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

var result = foo.Value;
Servy
источник
Помните, что это Lazyследует использовать для значений, требующих большой вычислительной мощности, и что вы не должны использовать их для взаимодействия (поскольку семантика .Valueзаключается в том, что он возвращает значение, подобное свойству, а не (интерактивное) действие). Вместо этого для таких действий следует использовать делегата.
Abel
1
@Abel Нет, это не для значений, требующих большой вычислительной мощности, это для любого значения, для которого вы хотели бы отложить инициализацию до тех пор, пока он не будет запрошен, и никогда не инициализировать это значение более одного раза. При этом значение Value будет использовано; это DialogResultполучено из окна сообщения. Основное различие между этим решением и использованием делегата заключается в том, следует ли повторно вычислять значение при каждом запросе или нет. Моя интерпретация требований заключалась в том, что это концептуальная инициализация значения, а не операция, которую нужно повторить.
Servy
Lazyможет быть легко использован неправильно. У него есть накладные расходы, поэтому использование его «просто» для отсрочки небольшой задачи вызовет больше накладных расходов, чем получит. Отображение ящиков сообщений из свойства - это (imo) плохая практика в целом, независимо от Lazy. Кстати, из MSDN цитирую: «Используйте ленивую инициализацию, чтобы отложить создание большого или ресурсоемкого объекта» . Вы можете не согласиться с этим, но изначально он был разработан именно для этого.
Abel
1
@Abel Накладные расходы на производительность Lazyв таком контексте, безусловно, незначительны; он будет бледнеть по сравнению со временем, проведенным в ожидании, пока человек нажмет на окно сообщения. В основном это сводится к реальным требованиям базового приложения; расплывчатость вопроса делает невозможным объективно правильный ответ. Это одна из интерпретаций вопроса. Что касается того, что делать много работы в приобретателе собственности плохо; видимо вы принципиально против всего дизайна Lazy. Добро пожаловать на это мнение.
Servy
Извините, вы, должно быть, неправильно меня поняли. Конечно, MessageBox накладные расходы незначительны (я бы просто не использовал UI внутри свойства). Я имел в виду небольшие задачи в целом (например, отсрочку 2 + 3 * 4 / i), когда накладные расходы на создание закрытия больше, чем сам расчет. И я думаю, что полностью согласен Lazy, на самом деле мы часто используем его в F # (немного меньше в C #), и мы на собственном горьком опыте узнали, что вы должны быть осторожны с этим, особенно в отношении производительности.
Abel
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);

... точно так же, как вы просили, с функцией (или здесь «Действие», поскольку она возвращает void), назначенной как переменная.

Zaccone
источник
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());
    }
  • Получим сборку, тип и метод Main:

    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

Амир Саниян
источник
Я не думаю, что компиляция динамического кода вообще имела отношение к заданному вопросу.
Иреванчи 03