将代码分配给变量


124

是否可以创建变量,并为其分配一行代码,例如:

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

...所以当我使用变量时,它将执行代码行。


100
+1是一种罕见的组合,它是第一次接触编码并提出一个好问题:您了解自己想做的事情并很好地解释了它,只是不知道它的用语,因此您无法独自找到它。
Tim S.

10
您要查找的术语是委托
Lasse V. Karlsen 2014年

stackoverflow.com/questions/6187944/…检查一下,我认为您需要足够的解释。在这方面,Asp的工作原理几乎与winforms一样。
CSharpie 2014年

听起来很像Objective-c中的方块
Brian Tracy

Answers:


89

您可以将其分配给Action这样的对象:

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

然后调用它:

ButtonClicked();

为了完整性(关于各种评论)...

如Erik所述,您可以执行多行代码:

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

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

正如Tim所言,您可以省略Action关键字

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

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

为了解决KRyan关于空括号的评论,该评论代表您希望发送给Action的参数列表(在本例中为none)

例如,如果您想指定要显示的消息,则可以添加“ message”作为参数(请注意, 为了指定单个字符串参数,我将其更改Action Action<string>

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

ButtonClicked("hello world!");

10
Action ButtonClicked = () => MessageBox.Show("hi");是等效的,并且是IMO更好的(如果愿意,可以添加括号)
Tim S.

1
该动作还可能解析为多个代码行。
Erik Philips

2
@CSharpie我不确定做出这样的假设是否对OP有帮助。
Erik Philips

2
@CSharpie为什么OP不能在其中使用它WinForms
vivat双鱼座

2
@CSharpie我明白你在说什么。如果他实际上是将其附加到Button.Click事件上,而不是将其存储在他碰巧命名的变量中ButtonClicked
vivat双鱼座

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.
}  

从这里开始,我们可以转到lambda表达式,看看它们在您的情况下如何有用:
.NET库已经定义了许多委托,其中有些像Action这样,它们不接受任何参数且不返回任何值。它的定义是,public delegate void Action();
您可以始终根据需要使用它,而不必每次都定义一个新的委托。例如,在前面的上下文中,您可能已经编写了

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

会做同样的事情。
现在您已经看到了如何使用委托的不同方法,让我们使用我们的第一个lambda表达式。Lambda表达式是匿名函数;因此,它们是正常功能,但没有名称。它们具有以下形式:

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

对不起,很长的帖子,希望它不会太混乱:)

编辑:我忘了提到一种替代形式,尽管它不经常使用,但它可以使lambda表达式更容易理解:

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

反过来,您可以使用lambda表达式,但是不需要(但在某些情况下可能)不需要定义参数的类型,例如,上面的代码可以简单地写为:

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是它返回的值类似于属性,而不是(交互)动作)。应该将委托用于此类操作。
Abel

1
@Abel不,它不是用于需要大量处理能力的值,而是用于您希望延迟其初始化直到请求其值的任何值,而永远不要多次初始化该值。这里的值Value 使用; 它是DialogResult从显示消息框收到的。此解决方案与使用委托之间的主要区别在于,是否应在每次请求值时重新计算该值。我对需求的解释是,这在概念上是初始化值,而不是要重复的操作
Servy

Lazy容易被错误使用。它本身具有开销,使用它“只是”延迟一个小任务将引起更多的开销。无论如何,从属性显示消息框都是(imo)不良做法Lazy。顺便说一句,来自MSDN的我引用:“使用延迟初始化来延迟大型或资源密集型对象的创建”。您可以不同意这一点,但这就是最初设计的目的。
亚伯

1
@Abel Lazy在这种情况下的性能开销当然可以忽略不计;与等待人类点击消息框所花费的时间相比,它会显得苍白。它主要取决于底层应用程序的实际需求。问题的模糊性使得不可能客观上正确地回答。这是对该问题的一种解释。至于在吸气剂中做很多工作是不好的;显然,您从根本上反对的整个设计Lazy。对此表示欢迎。
2014年

抱歉,您一定误会了我。当然,MessageBox 开销可以忽略不计(我只是不会在属性内使用UI)。我的意思是一般的小任务(例如deferring 2 + 3 * 4 / i),其中创建闭包的开销大于计算本身。而且我想我完全拥护Lazy,实际上我们在F#中使用了很多(在C#中很少使用),并且我们已经学会了必须谨慎使用的困难方法,尤其是。在性能方面。
Abel 2014年

4

我阅读您的问题的方式是在GUI控件的上下文中进行的?

如果在WPF中,请查看处理来自控件的命令的“正确”方法:http : //msdn.microsoft.com/zh-cn/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);

...就像您在询问时一样,将一个函数(或此处为“ Action”,因为它返回void)分配为变量。


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


我认为动态代码编译与所提问题根本无关。
Iravanchi 2014年
By using our site, you acknowledge that you have read and understand our Cookie Policy and Privacy Policy.
Licensed under cc by-sa 3.0 with attribution required.