เป็นไปได้ไหมที่จะสร้างตัวแปรและกำหนดบรรทัดของรหัสให้กับตัวแปรเช่น:
ButtonClicked = (MessageBox.Show("Hello, World!"));
... ดังนั้นเมื่อฉันใช้ตัวแปรมันจะดำเนินการบรรทัดของรหัส
เป็นไปได้ไหมที่จะสร้างตัวแปรและกำหนดบรรทัดของรหัสให้กับตัวแปรเช่น:
ButtonClicked = (MessageBox.Show("Hello, World!"));
... ดังนั้นเมื่อฉันใช้ตัวแปรมันจะดำเนินการบรรทัดของรหัส
คำตอบ:
คุณสามารถกำหนดให้เป็น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
เป็น เพื่อระบุพารามิเตอร์สตริงเดียว) :Action<string>
Action<string> ButtonClicked = (message) => MessageBox.Show(message);
ButtonClicked("hello world!");
Action ButtonClicked = () => MessageBox.Show("hi");
เทียบเท่าและ IMO ดีกว่า (เพิ่ม parens ถ้าคุณต้องการ)
WinForms
ไม่ได้?
Button.Click
ButtonClicked
ในกรณีของคุณคุณต้องการใช้ไฟล์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");
})();
นอกจากนี้การใช้ generics:
// 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);
แก้ไข 3: ฉันรู้ว่าโพสต์นี้อยู่ที่นี่มาระยะหนึ่งแล้ว แต่ฉันคิดว่านี่เป็นเรื่องที่ดีจริงๆที่จะไม่พูดถึง: คุณสามารถทำได้ซึ่งส่วนใหญ่เกี่ยวข้องกับคำถามของคุณ:
dynamic aFunction = (Func<string, DialogResult>)MessageBox.Show;
aFunction("Hello, world!");
หรือเพียงแค่:
Func<string, DialogResult> aFunction = MessageBox.Show;
aFunction("Hello, world!");
Lazy
ชั้นถูกออกแบบมาเพื่อเป็นตัวแทนของความคุ้มค่าที่จะไม่ถูกคำนวณจนกว่าคุณขอมัน คุณสร้างมันโดยระบุวิธีการที่กำหนดว่าควรจะสร้างอย่างไร แต่จะจัดการกับการเรียกใช้เมธอดนั้นไม่เกินหนึ่งครั้ง (แม้ในหลายเธรดที่ร้องขอค่า) และเพียงแค่ส่งคืนค่าที่สร้างไว้แล้วสำหรับคำขอเพิ่มเติมใด ๆ :
var foo = new Lazy<DialogResult>(()=>MessageBox.Show("Hello, World!"));
var result = foo.Value;
Lazy
ควรใช้สำหรับค่าที่ต้องใช้พลังในการประมวลผลจำนวนมากและคุณไม่ควรใช้เพื่อการโต้ตอบ (เนื่องจากความหมาย.Value
คือการส่งคืนค่าคล้ายกับคุณสมบัติไม่ใช่การกระทำ (โต้ตอบ)) ควรใช้ผู้รับมอบสิทธิ์ในการดำเนินการดังกล่าวแทน
Value
มีการใช้; เป็นสิ่งที่DialogResult
ได้รับจากการแสดงกล่องข้อความ ความแตกต่างหลักระหว่างโซลูชันนี้และการใช้ผู้รับมอบสิทธิ์คือควรคำนวณค่าใหม่ทุกครั้งที่มีการร้องขอหรือไม่ การตีความข้อกำหนดของฉันคือว่านี่เป็นการเริ่มต้นค่าตามแนวคิดไม่ใช่การดำเนินการที่จะทำซ้ำ
Lazy
สามารถใช้ผิดได้ง่าย มันมีค่าใช้จ่ายในตัวเองการใช้ "เพียง" เพื่อเลื่อนงานเล็ก ๆ น้อย ๆ จะทำให้เกิดค่าใช้จ่ายมากกว่าที่จะได้รับ แสดงข้อความการจากคุณสมบัติถูก (IMO) Lazy
ปฏิบัติไม่ดีโดยทั่วไปโดยไม่คำนึงถึง Btw จาก MSDN ฉันพูด: "ใช้เริ่มต้นขี้เกียจที่จะเลื่อนการสร้างวัตถุขนาดใหญ่หรือทรัพยากรอย่างเข้มข้น" คุณไม่เห็นด้วยก็ได้ แต่นั่นคือสิ่งที่ออกแบบมาสำหรับตอนแรก
Lazy
ในการแสดงสำหรับในบริบทเช่นนี้เป็นเรื่องเล็กน้อยอย่างแน่นอน มันจะซีดลงเมื่อเทียบกับเวลาที่รอให้มนุษย์คลิกที่กล่องข้อความ ส่วนใหญ่จะเป็นไปตามข้อกำหนดที่แท้จริงของแอปพลิเคชันพื้นฐาน ความคลุมเครือของคำถามทำให้คำตอบที่ถูกต้องเป็นไปไม่ได้ นี่คือการตีความคำถามอย่างหนึ่ง สำหรับการทำงานจำนวนมากในทรัพย์สินที่ได้รับความเสียหาย; Lazy
เห็นได้ชัดว่าคุณไม่เห็นด้วยพื้นฐานการออกแบบทั้งหมดของ ยินดีต้อนรับสู่ความคิดเห็นนั้น
MessageBox
ค่าโสหุ้ยนั้นเล็กน้อยมาก (ฉันจะไม่ใช้ UI ภายในคุณสมบัติ) ฉันหมายถึงงานเล็ก ๆ โดยทั่วไป (เช่นการเลื่อนเวลาออกไป2 + 3 * 4 / i
) ซึ่งค่าใช้จ่ายในการสร้างการปิดนั้นมากกว่าการคำนวณเอง และฉันคิดว่าฉันยอมรับอย่างเต็มที่Lazy
ในความเป็นจริงเราใช้มันมากใน F # (น้อยกว่าเล็กน้อยใน C #) และเราได้เรียนรู้วิธีที่ยากที่คุณต้องระวังโดยเฉพาะ ในแง่ของประสิทธิภาพ
วิธีที่ฉันอ่านคำถามของคุณนี่อยู่ในบริบทของการควบคุม GUI หรือไม่?
หากอยู่ใน 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);
... เช่นเดียวกับที่คุณถามโดยมีฟังก์ชัน (หรือที่นี่คือ "Action" เนื่องจากจะคืนค่าเป็นโมฆะ) ที่กำหนดให้เป็นตัวแปร
คุณสามารถกำหนดรหัส 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