การกำหนดรหัสให้กับตัวแปร


124

เป็นไปได้ไหมที่จะสร้างตัวแปรและกำหนดบรรทัดของรหัสให้กับตัวแปรเช่น:

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

... ดังนั้นเมื่อฉันใช้ตัวแปรมันจะดำเนินการบรรทัดของรหัส


100
+1 สำหรับการรวมกันที่หายากของการเป็นมือใหม่ในการเขียนโค้ดและการถามคำถามที่ดี: คุณเข้าใจสิ่งที่คุณต้องการทำและอธิบายได้ดีคุณไม่รู้คำศัพท์เฉพาะดังนั้นคุณจึงไม่สามารถค้นหาได้ด้วยตัวเอง
Tim S.

10
คำที่คุณกำลังมองหาเป็นผู้รับมอบสิทธิ์
Lasse V.Karlsen

stackoverflow.com/questions/6187944/…ตรวจสอบสิ่งนี้ฉันคิดว่ามีคำอธิบายเพียงพอที่คุณต้องการ Asp ทำงานเกือบจะเหมือนกับ winforms ในเรื่องนั้น
CSharpie

ฟังดูคล้ายกับบล็อกใน Objective-c
Brian Tracy

คำตอบ:


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 เป็น เพื่อระบุพารามิเตอร์สตริงเดียว) :Action<string>

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

ButtonClicked("hello world!");

10
Action ButtonClicked = () => MessageBox.Show("hi");เทียบเท่าและ IMO ดีกว่า (เพิ่ม parens ถ้าคุณต้องการ)
Tim S.

1
นอกจากนี้ยังเป็นไปได้สำหรับการดำเนินการเพื่อแก้ไขโค้ดมากกว่าหนึ่งบรรทัด
Erik Philips

2
@CSharpie ฉันไม่แน่ใจว่าการตั้งสมมติฐานดังกล่าวจะเป็นประโยชน์สำหรับ OP
Erik Philips

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

นอกจากนี้การใช้ 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!");

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ปฏิบัติไม่ดีโดยทั่วไปโดยไม่คำนึงถึง Btw จาก MSDN ฉันพูด: "ใช้เริ่มต้นขี้เกียจที่จะเลื่อนการสร้างวัตถุขนาดใหญ่หรือทรัพยากรอย่างเข้มข้น" คุณไม่เห็นด้วยก็ได้ แต่นั่นคือสิ่งที่ออกแบบมาสำหรับตอนแรก
Abel

1
@Abel ค่าใช้จ่ายLazyในการแสดงสำหรับในบริบทเช่นนี้เป็นเรื่องเล็กน้อยอย่างแน่นอน มันจะซีดลงเมื่อเทียบกับเวลาที่รอให้มนุษย์คลิกที่กล่องข้อความ ส่วนใหญ่จะเป็นไปตามข้อกำหนดที่แท้จริงของแอปพลิเคชันพื้นฐาน ความคลุมเครือของคำถามทำให้คำตอบที่ถูกต้องเป็นไปไม่ได้ นี่คือการตีความคำถามอย่างหนึ่ง สำหรับการทำงานจำนวนมากในทรัพย์สินที่ได้รับความเสียหาย; Lazyเห็นได้ชัดว่าคุณไม่เห็นด้วยพื้นฐานการออกแบบทั้งหมดของ ยินดีต้อนรับสู่ความคิดเห็นนั้น
Servy

ขออภัยคุณต้องเข้าใจผิดฉัน แน่นอนว่าด้วยMessageBox ค่าโสหุ้ยนั้นเล็กน้อยมาก (ฉันจะไม่ใช้ UI ภายในคุณสมบัติ) ฉันหมายถึงงานเล็ก ๆ โดยทั่วไป (เช่นการเลื่อนเวลาออกไป2 + 3 * 4 / i) ซึ่งค่าใช้จ่ายในการสร้างการปิดนั้นมากกว่าการคำนวณเอง และฉันคิดว่าฉันยอมรับอย่างเต็มที่Lazyในความเป็นจริงเราใช้มันมากใน F # (น้อยกว่าเล็กน้อยใน C #) และเราได้เรียนรู้วิธีที่ยากที่คุณต้องระวังโดยเฉพาะ ในแง่ของประสิทธิภาพ
Abel

4

วิธีที่ฉันอ่านคำถามของคุณนี่อยู่ในบริบทของการควบคุม 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" เนื่องจากจะคืนค่าเป็นโมฆะ) ที่กำหนดให้เป็นตัวแปร


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


ฉันไม่คิดว่าการคอมไพล์โค้ดแบบไดนามิกเกี่ยวข้องกับคำถามที่ถามเลย
Iravanchi
โดยการใช้ไซต์ของเรา หมายความว่าคุณได้อ่านและทำความเข้าใจนโยบายคุกกี้และนโยบายความเป็นส่วนตัวของเราแล้ว
Licensed under cc by-sa 3.0 with attribution required.