Menetapkan kode ke variabel

124

Apakah mungkin untuk membuat variabel, dan memberikan sebaris kode padanya, seperti:

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

... jadi ketika saya menggunakan variabel, itu akan mengeksekusi baris kode.

pengguna3539891
sumber
100
+1 untuk kombinasi langka menjadi baru dalam pengkodean dan mengajukan pertanyaan yang bagus: Anda memahami apa yang ingin Anda lakukan dan menjelaskannya dengan baik, Anda hanya tidak tahu istilahnya sehingga Anda tidak dapat menemukannya sendiri.
Tim S.
10
Istilah yang Anda cari adalah delegasi .
Lasse V. Karlsen
stackoverflow.com/questions/6187944/… lihat ini, saya rasa ada cukup penjelasan yang Anda perlukan. Asp bekerja hampir seperti winforms dalam hal ini.
CSharpie
Kedengarannya sangat mirip blok di Objective-c
Brian Tracy

Jawaban:

89

Anda dapat menetapkannya Actionseperti ini:

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

Kemudian sebut saja:

ButtonClicked();

Untuk kelengkapan (sehubungan dengan berbagai komentar) ...

Seperti yang dikatakan Erik, Anda dapat mengeksekusi beberapa baris kode:

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

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

Seperti yang dikatakan Tim, Anda dapat menghilangkan Actionkata kunci tersebut

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

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

Untuk mengatasi komentar KRyan, mengenai tanda kurung kosong, yang mewakili daftar parameter yang ingin Anda kirimkan ke Action (dalam hal ini, tidak ada) .

Jika, misalnya, Anda ingin menentukan pesan ke acara, Anda bisa menambahkan "pesan" sebagai parameter (catatan bahwa saya berubah Action untuk untuk menentukan parameter string tunggal) :Action<string>

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

ButtonClicked("hello world!");
vivat pisces
sumber
10
Action ButtonClicked = () => MessageBox.Show("hi");setara dan IMO lebih baik (tambahkan parens jika Anda suka)
Tim S.
1
Tindakan juga dapat diselesaikan ke lebih dari satu baris kode.
Erik Philips
2
@CSharpie Saya tidak yakin bahwa membuat asumsi itu berguna untuk OP.
Erik Philips
2
@CSharpie Mengapa OP tidak menggunakan ini WinForms?
vivat pisces
2
@CSharpie Saya melihat apa yang Anda katakan. Jika dia benar-benar melampirkan ini ke Button.Clickacara, dan tidak menyimpannya dalam variabel yang kebetulan dia beri nama ButtonClicked.
vivat pisces
51

Dalam kasus Anda, Anda ingin menggunakan file delegate.

Mari kita lihat bagaimana seorang delegasi bekerja dan bagaimana kita bisa mendapatkan formulir yang lebih mudah dengan memahami konsepnya:

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

Anda lihat, delegasi mengambil bentuk fungsi normal tetapi tanpa argumen apa pun (Ini bisa menggunakan sejumlah argumen seperti metode lain, tetapi demi kesederhanaan, tidak).

Sekarang, mari gunakan apa yang kita miliki; kita akan mendefinisikan delegasi seperti kita mendefinisikan variabel lainnya:

ButtonClick ButtonClicked = new ButtonClick(OnButtonClick);

Kami pada dasarnya membuat variabel baru bernama ButtonClicked, yang memiliki jenis ButtonClick (yang merupakan delegasi) dan ketika digunakan, akan mengeksekusi metode dalam metode OnButtonClick ().
Untuk menggunakannya kita cukup memanggil:ButtonClicked();

Jadi keseluruhan kodenya adalah:

delegate void ButtonClick();

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

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

Dari sini, kita dapat beralih ke ekspresi lambda dan melihat bagaimana mereka dapat berguna dalam situasi Anda:
Ada banyak delegasi yang sudah ditentukan oleh pustaka .NET, dengan beberapa seperti Action, yang tidak menerima parameter apa pun dan tidak mengembalikan nilai. Ini didefinisikan sebagai public delegate void Action();
Anda selalu dapat menggunakannya untuk kebutuhan Anda alih-alih kebutuhan untuk menentukan delegasi baru setiap saat. Dalam konteks sebelumnya misalnya, Anda bisa saja baru saja menulis

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

yang akan melakukan hal yang sama.
Sekarang Anda telah melihat berbagai cara menggunakan delegasi, mari gunakan ekspresi lambda pertama kita. Ekspresi lambda adalah fungsi anonim; jadi, itu adalah fungsi normal tetapi tanpa nama. Mereka adalah dari bentuk-bentuk itu:

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

Dalam kasus kami, kami tidak memiliki parameter apa pun jadi kami akan menggunakan ekspresi terakhir. Kita bisa menggunakan ini hanya sebagai fungsi OnButtonClick, tapi kita mendapat keuntungan karena tidak memiliki fungsi bernama. Sebagai gantinya kita bisa melakukan sesuatu seperti ini:

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

atau bahkan lebih mudah,

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

maka cukup panggil ButtonClicked();Tentu saja Anda juga dapat memiliki beberapa baris kode, tetapi saya tidak ingin membingungkan Anda lagi. Akan terlihat seperti ini:

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

Anda juga bisa bermain-main, misalnya, Anda dapat menjalankan fungsi seperti ini:

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

Maaf untuk postingan yang panjang, semoga tidak terlalu membingungkan :)

EDIT: Saya lupa menyebutkan bahwa bentuk alternatif yang, meskipun tidak sering digunakan, dapat membuat ekspresi lambda lebih mudah dipahami:

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

Juga, menggunakan obat generik:

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

Pada gilirannya, Anda dapat menggunakan ekspresi lambda, tetapi Anda tidak perlu (tetapi mungkin dalam beberapa kasus) untuk menentukan jenis parameter, misalnya, kode di atas dapat dengan mudah ditulis sebagai:

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

atau:

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

EDIT2:
Action<string>adalah representasi dari public void delegate Action(string obj);
Action<string,string>adalah representasi public void delegate Action(string obj, string obj2);
Secara umum, Action<T>adalah representasi daripublic void delegate Action<T>(T obj);

EDIT3: Saya tahu posnya sudah ada di sini untuk sementara waktu, tetapi saya pikir ini sangat keren untuk tidak disebutkan: Anda dapat melakukan ini, yang sebagian besar terkait dengan pertanyaan Anda:

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

atau sederhana:

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

The Lazykelas khusus dirancang untuk mewakili nilai yang tidak akan dihitung sampai Anda meminta untuk itu. Anda membangunnya dengan menyediakan metode yang menentukan bagaimana itu harus dibangun, tetapi itu akan menangani eksekusi metode itu tidak lebih dari sekali (bahkan saat menghadapi beberapa utas yang meminta nilai) dan hanya mengembalikan nilai yang sudah dibangun untuk permintaan tambahan apa pun:

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

var result = foo.Value;
Pelayanan
sumber
Ingat itu Lazyharus digunakan untuk nilai yang membutuhkan banyak daya pemrosesan, dan Anda tidak boleh menggunakannya untuk interaksi (karena semantiknya .Valueadalah ia mengembalikan nilai, mirip dengan properti, bukan tindakan (interaktif)). Sebagai gantinya, seorang delegasi harus digunakan untuk tindakan semacam itu.
Abel
1
@Abel Tidak, ini bukan untuk nilai yang membutuhkan banyak daya pemrosesan, ini untuk nilai apa pun yang ingin Anda tunda inisialisasi hingga diminta, sementara tidak pernah menginisialisasi nilai itu lebih dari sekali. Sini nilai Value yang digunakan; itu DialogResultditerima dari menampilkan kotak pesan. Perbedaan utama antara solusi ini dan penggunaan delegasi adalah apakah nilai harus dihitung ulang setiap kali diminta atau tidak. Interpretasi saya tentang persyaratan adalah bahwa ini secara konseptual menginisialisasi nilai, bukan operasi yang akan diulang.
Pelayanan
Lazydapat dengan mudah digunakan secara salah. Ini memiliki overhead sendiri, menggunakannya "hanya" untuk menunda tugas kecil akan meminta lebih banyak overhead daripada keuntungannya. Menampilkan kotak pesan dari suatu properti adalah (imo) praktik buruk secara umum, terlepas dari Lazy. Btw, dari MSDN, saya mengutip: "Gunakan inisialisasi malas untuk menunda pembuatan objek yang besar atau intensif sumber daya" . Anda bisa saja tidak setuju dengan itu, tapi itu memang awalnya dirancang untuk itu.
Abel
1
@Abel Overhead kinerja untuk Lazykonteks seperti ini tentunya dapat diabaikan; itu akan sepi dibandingkan dengan waktu yang dihabiskan menunggu manusia untuk mengklik kotak pesan. Ini sebagian besar bermuara pada persyaratan nyata dari aplikasi yang mendasarinya; ketidakjelasan pertanyaan membuat jawaban yang benar secara objektif menjadi tidak mungkin. Ini adalah salah satu interpretasi dari pertanyaan tersebut. Adapun melakukan banyak pekerjaan di properti getter menjadi buruk; tampaknya Anda pada dasarnya menentang keseluruhan desain Lazy. Terima kasih atas opini itu.
Pelayanan
Maaf, Anda pasti salah paham dengan saya. Tentu saja, dengan MessageBox overhead dapat diabaikan (saya tidak akan menggunakan UI di dalam properti). Maksud saya tugas-tugas kecil secara umum (seperti menunda 2 + 3 * 4 / i), di mana biaya pembuatan penutupan lebih besar daripada perhitungan itu sendiri. Dan saya pikir saya sepenuhnya merangkul Lazy, pada kenyataannya kami banyak menggunakannya di F # (sedikit lebih sedikit di C #) dan kami telah belajar dengan cara yang sulit bahwa Anda harus berhati-hati dengannya, khususnya. sehubungan dengan kinerja.
Abel
4

Cara saya membaca pertanyaan Anda, apakah ini dalam konteks kontrol GUI?

Jika ini ada di WPF, lihat cara yang "benar" untuk menangani perintah dari kontrol: http://msdn.microsoft.com/en-us/library/ms752308(v=vs.110).aspx

... tapi itu bisa menjadi sakit dan berlebihan. Untuk kasus umum yang lebih sederhana, Anda mungkin mencari pengendali event, seperti:

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

Penangan kejadian itu dapat ditangani dengan berbagai cara. Contoh di atas menggunakan fungsi anonim, tetapi Anda juga dapat melakukan:

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

... seperti yang Anda tanyakan, dengan fungsi (atau di sini, "Action", karena mengembalikan void) yang ditetapkan sebagai variabel.

Zaccone
sumber
1

Anda dapat menetapkan kode C # ke variabel, mengkompilasinya saat runtime dan menjalankan kode:

  • Tulis kode Anda:

    // Assign C# code to the code variable.
    string code = @"
    using System;
    
    namespace First
    {
        public class Program
        {
            public static void Main()
            {
                " +
                "Console.WriteLine(\"Hello, world!\");"
                + @"
            }
        }
    }
    ";
  • Buat penyedia dan parameter kompilator:

    CSharpCodeProvider provider = new CSharpCodeProvider();
    CompilerParameters parameters = new CompilerParameters();
  • Tentukan parameter kompilator:

    // 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;
  • Kompilasi perakitan:

    CompilerResults results = provider.CompileAssemblyFromSource(parameters, code);
  • Periksa kesalahan:

    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());
    }
  • Dapatkan perakitan, jenis dan metode Utama:

    Assembly assembly = results.CompiledAssembly;
    Type program = assembly.GetType("First.Program");
    MethodInfo main = program.GetMethod("Main");
  • Menjalankannya:

    main.Invoke(null, null);

Referensi:

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

Amir Saniyan
sumber
Saya tidak berpikir kompilasi kode dinamis sama sekali tidak relevan dengan pertanyaan yang diajukan.
Iravanchi