Gán mã cho một biến


124

Có thể tạo một biến và gán một dòng mã cho nó, chẳng hạn như:

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

... vì vậy khi tôi sử dụng biến, nó sẽ thực thi dòng mã.


100
+1 cho sự kết hợp hiếm hoi của việc mới viết mã và hỏi một câu hỏi hay: bạn hiểu những gì bạn muốn làm và giải thích nó tốt, bạn chỉ không biết thuật ngữ cho nó để bạn không thể tự mình tìm thấy nó.
Tim S.

10
Thuật ngữ bạn đang tìm kiếm là một đại biểu .
Lasse V. Karlsen

stackoverflow.com/questions/6187944/ Hãy kiểm tra cái này, tôi nghĩ có đủ sự khám phá mà bạn sẽ cần. Asp hoạt động gần giống như winforms trong vấn đề đó.
CSharpie

Âm thanh rất giống các khối trong Objective-c
Brian Tracy

Câu trả lời:


89

Bạn có thể gán nó cho Actionnhư thế này:

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

Sau đó gọi nó:

ButtonClicked();

Để hoàn thiện (liên quan đến các ý kiến ​​khác nhau) ...

Như Erik đã nêu, bạn có thể thực thi nhiều dòng mã:

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

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

Như Tim đã nêu, bạn có thể bỏ qua Actiontừ khóa

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

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

Để giải quyết nhận xét của KRyan, liên quan đến dấu ngoặc đơn trống, đại diện cho danh sách các tham số bạn muốn có thể gửi tới Hành động (trong trường hợp này là không có) .

Nếu, ví dụ, bạn muốn xác định thông điệp tới chương trình, bạn có thể thêm "thông báo" như một tham số (lưu ý rằng tôi đã thay đổi Action để nhằm xác định một tham số chuỗi đơn) :Action<string>

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

ButtonClicked("hello world!");

10
Action ButtonClicked = () => MessageBox.Show("hi");là tương đương và IMO đẹp hơn (thêm parens nếu bạn thích)
Tim S.

1
Cũng có thể hành động giải quyết nhiều hơn một dòng mã.
Erik Philips

2
@CSharpie Tôi không chắc rằng việc đưa ra giả định đó có ích cho OP.
Erik Philips

2
@CSharpie Tại sao OP không thể sử dụng cái này trong WinForms?
Song Ngư vivat

2
@CSharpie Tôi thấy những gì bạn đang nói. Nếu anh ta thực sự gắn nó vào một Button.Clicksự kiện, và không lưu trữ nó trong một biến mà anh ta đã đặt tên ButtonClicked.
Song Ngư vivat

51

Trong trường hợp của bạn, bạn muốn sử dụng a delegate.

Chúng ta hãy xem cách một đại biểu hoạt động và làm thế nào chúng ta có thể đến một hình thức dễ dàng hơn bằng cách hiểu khái niệm của nó:

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

Bạn thấy đấy, đại biểu có dạng hàm bình thường nhưng không có bất kỳ đối số nào (Nó có thể lấy bất kỳ số lượng đối số nào giống như bất kỳ phương thức nào khác, nhưng vì đơn giản, nó không).

Bây giờ, hãy sử dụng những gì chúng ta có; chúng tôi sẽ định nghĩa đại biểu giống như chúng tôi xác định bất kỳ biến nào khác:

ButtonClick ButtonClicked = new ButtonClick(OnButtonClick);

Về cơ bản, chúng tôi đã tạo một biến mới có tên là NútClicky, có một loại Nút bấm (là đại biểu) và khi được sử dụng, sẽ thực thi phương thức trong phương thức OnButtonClick ().
Để sử dụng nó, chúng tôi chỉ cần gọi:ButtonClicked();

Vì vậy, toàn bộ mã sẽ là:

delegate void ButtonClick();

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

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

Từ đây, chúng ta có thể chuyển sang các biểu thức lambda và xem chúng có thể hữu ích như thế nào trong tình huống của bạn:
Có nhiều đại biểu đã được xác định bởi các thư viện .NET, với một số như Action, không chấp nhận bất kỳ tham số nào và không trả về giá trị. Nó được định nghĩa là public delegate void Action();
Bạn luôn có thể sử dụng nó cho nhu cầu của mình thay vì nhu cầu xác định một đại biểu mới mỗi lần. Trong bối cảnh trước đó, ví dụ, bạn có thể vừa viết

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

mà sẽ làm như vậy.
Bây giờ bạn đã thấy các cách khác nhau về cách sử dụng đại biểu, hãy sử dụng biểu thức lambda đầu tiên của chúng tôi. Biểu thức Lambda là các hàm ẩn danh; vì vậy, chúng là các chức năng bình thường nhưng không có tên. Chúng là những hình thức:

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

Trong trường hợp của chúng tôi, chúng tôi không có bất kỳ tham số nào nên chúng tôi sẽ sử dụng biểu thức cuối cùng. Chúng tôi có thể sử dụng chức năng này giống như chức năng OnButtonClick, nhưng chúng tôi có lợi thế là không có chức năng được đặt tên. Thay vào đó chúng ta có thể làm một cái gì đó như thế này:

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

hoặc thậm chí dễ dàng hơn,

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

sau đó chỉ cần gọi ButtonClicked();Tất nhiên bạn cũng có thể có nhiều dòng mã, nhưng tôi không muốn làm bạn bối rối hơn. Nó sẽ trông như thế này mặc dù:

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

Bạn cũng có thể chơi xung quanh, ví dụ, bạn có thể thực thi một chức năng như thế này:

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

Xin lỗi vì bài viết dài, hy vọng nó không quá khó hiểu :)

EDIT: Tôi quên đề cập rằng một hình thức thay thế, mặc dù không thường xuyên được sử dụng, có thể làm cho biểu thức lambda dễ hiểu hơn:

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

Ngoài ra, sử dụng thuốc generic:

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

Đổi lại, bạn có thể sử dụng các biểu thức lambda, nhưng bạn không cần (nhưng có thể trong một số trường hợp) để xác định loại tham số, ví dụ, mã ở trên có thể được viết đơn giản là:

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

hoặc là:

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

EDIT2:
Action<string>là một đại diện của public void delegate Action(string obj);
Action<string,string>là một đại diện của public void delegate Action(string obj, string obj2);
Nói chung, Action<T>là một đại diện củapublic void delegate Action<T>(T obj);

EDIT3: Tôi biết bài đăng đã ở đây được một thời gian, nhưng tôi nghĩ điều này thực sự tuyệt vời khi không đề cập đến: Bạn có thể làm điều này, chủ yếu liên quan đến câu hỏi của bạn:

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

hoặc đơn giản:

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

7

Các Lazylớp học được thiết kế đặc biệt để đại diện cho một giá trị đó sẽ không được tính cho đến khi bạn yêu cầu nó. Bạn xây dựng nó bằng cách cung cấp một phương thức xác định cách nó sẽ được xây dựng, nhưng nó sẽ xử lý việc thực thi phương thức đó không quá một lần (ngay cả khi đối mặt với nhiều luồng yêu cầu giá trị) và chỉ cần trả về giá trị đã được xây dựng cho bất kỳ yêu cầu bổ sung nào:

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

var result = foo.Value;

Hãy nhớ rằng Lazynên được sử dụng cho các giá trị đòi hỏi nhiều sức mạnh xử lý và bạn không nên sử dụng chúng cho tương tác (vì ngữ nghĩa .Valuelà nó trả về một giá trị, tương tự như một thuộc tính, không phải là hành động (tương tác)). Một đại biểu nên được sử dụng cho các hành động như vậy thay thế.
Abel

1
@Abel Không, nó không dành cho các giá trị đòi hỏi nhiều sức mạnh xử lý, nó dành cho bất kỳ giá trị nào bạn muốn trì hoãn việc khởi tạo cho đến khi được yêu cầu, trong khi không bao giờ khởi tạo giá trị đó nhiều hơn một lần. Ở đây giá trị của Value được sử dụng; nó là DialogResultnhận được từ hiển thị hộp tin nhắn. Sự khác biệt chính giữa giải pháp này và sử dụng một đại biểu là liệu giá trị có nên được tính lại mỗi lần nó được yêu cầu hay không. Giải thích của tôi về các yêu cầu là đây là khái niệm khởi tạo một giá trị, không phải là một hoạt động được lặp lại.
Phục vụ

Lazycó thể dễ dàng sử dụng sai. Nó có chi phí hoạt động, sử dụng nó "chỉ" để trì hoãn một nhiệm vụ nhỏ sẽ gọi nhiều chi phí hơn mức tăng. Hiển thị hộp tin nhắn từ một tài sản là (imo) thực tế xấu nói chung, bất kể Lazy. Btw, từ MSDN, tôi trích dẫn: "Sử dụng khởi tạo lười biếng để trì hoãn việc tạo ra một đối tượng lớn hoặc tốn nhiều tài nguyên" . Bạn có thể không đồng ý với điều đó, nhưng đó là những gì nó được thiết kế cho ban đầu.
Abel

1
@Abel Chi phí hoạt động Lazytrong bối cảnh như thế này chắc chắn là không đáng kể; nó sẽ nhạt đi so với thời gian chờ đợi của một người nhấp vào hộp tin nhắn. Nó chủ yếu đi vào các yêu cầu thực tế của ứng dụng cơ bản; sự mơ hồ của câu hỏi làm cho một câu trả lời đúng khách quan là không thể. Đây là một cách giải thích của câu hỏi. Đối với việc làm rất nhiều công việc trong một tài sản getter là xấu; rõ ràng là về cơ bản bạn trái ngược với toàn bộ thiết kế Lazy. Bạn hoan nghênh ý kiến ​​đó.
Phục vụ

Xin lỗi, bạn phải hiểu lầm tôi. Chắc chắn, với MessageBox chi phí không đáng kể (tôi chỉ không sử dụng UI bên trong một tài sản). Tôi có nghĩa là các nhiệm vụ nhỏ nói chung (như trì hoãn 2 + 3 * 4 / i), trong đó chi phí tạo ra bao đóng lớn hơn chính phép tính. Và tôi nghĩ rằng tôi hoàn toàn chấp nhận Lazy, trên thực tế chúng tôi sử dụng nó rất nhiều trong F # (ít hơn một chút trong C #) và chúng tôi đã học được cách khó khăn mà bạn phải cẩn thận với nó, đặc biệt. đối với hiệu suất.
Abel

4

Cách tôi đọc câu hỏi của bạn, đây là trong bối cảnh điều khiển GUI?

Nếu đây là trong WPF, hãy xem cách "đúng" để xử lý các lệnh từ các điều khiển: http://msdn.microsoft.com/en-us/l Library / ms752308 (v = vs.110) .aspx

... nhưng đó có thể là một nỗi đau và quá mức cần thiết. Đối với trường hợp chung đơn giản hơn, bạn có thể đang tìm kiếm một trình xử lý sự kiện, như:

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

Trình xử lý sự kiện đó có thể được xử lý bằng nhiều cách khác nhau. Ví dụ trên sử dụng hàm ẩn danh, nhưng bạn cũng có thể làm:

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

... Giống như bạn đã hỏi, với một hàm (hoặc ở đây, "Hành động", vì nó trả về khoảng trống) được gán dưới dạng một biến.


1

Bạn có thể gán mã C # cho một biến, biên dịch nó trong thời gian chạy và chạy mã:

  • Viết mã của bạn:

    // Assign C# code to the code variable.
    string code = @"
    using System;
    
    namespace First
    {
        public class Program
        {
            public static void Main()
            {
                " +
                "Console.WriteLine(\"Hello, world!\");"
                + @"
            }
        }
    }
    ";
  • Tạo nhà cung cấp và các tham số của trình biên dịch:

    CSharpCodeProvider provider = new CSharpCodeProvider();
    CompilerParameters parameters = new CompilerParameters();
  • Xác định tham số của trình biên dịch:

    // 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;
  • Biên dịch lắp ráp:

    CompilerResults results = provider.CompileAssemblyFromSource(parameters, code);
  • Kiểm tra lỗi:

    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());
    }
  • Nhận lắp ráp, loại và phương pháp chính:

    Assembly assembly = results.CompiledAssembly;
    Type program = assembly.GetType("First.Program");
    MethodInfo main = program.GetMethod("Main");
  • Chạy nó:

    main.Invoke(null, null);

Tài liệu tham khảo:

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


Tôi không nghĩ việc biên dịch mã động hoàn toàn phù hợp với câu hỏi được hỏi.
Margarvanchi
Khi sử dụng trang web của chúng tôi, bạn xác nhận rằng bạn đã đọc và hiểu Chính sách cookieChính sách bảo mật của chúng tôi.
Licensed under cc by-sa 3.0 with attribution required.