วิธีไม่ระบุชื่อในการเรียกใช้


131

มีปัญหาเล็กน้อยกับไวยากรณ์ที่เราต้องการเรียกผู้รับมอบสิทธิ์โดยไม่ระบุชื่อภายใน Control.Invoke

เราได้ลองใช้วิธีการที่แตกต่างกันมากมายซึ่งไม่เป็นประโยชน์

ตัวอย่างเช่น:

myControl.Invoke(delegate() { MyMethod(this, new MyEventArgs(someParameter)); }); 

โดยที่ someParameter เป็นโลคัลสำหรับเมธอดนี้

ด้านบนจะส่งผลให้เกิดข้อผิดพลาดของคอมไพเลอร์:

ไม่สามารถแปลงวิธีที่ไม่ระบุชื่อให้พิมพ์ 'System.Delegate' เนื่องจากไม่ใช่ประเภทผู้รับมอบสิทธิ์

คำตอบ:


221

เนื่องจากInvoke/ BeginInvokeaccepts Delegate(แทนที่จะเป็นผู้รับมอบสิทธิ์ที่พิมพ์) คุณต้องบอกคอมไพเลอร์ว่าผู้รับมอบสิทธิ์ประเภทใดที่จะสร้าง MethodInvoker(2.0) หรือAction(3.5) เป็นตัวเลือกทั่วไป (โปรดทราบว่ามีลายเซ็นเดียวกัน) ชอบมาก:

control.Invoke((MethodInvoker) delegate {this.Text = "Hi";});

หากคุณต้องการส่งผ่านพารามิเตอร์ "จับตัวแปร" เป็นวิธี:

string message = "Hi";
control.Invoke((MethodInvoker) delegate {this.Text = message;});

(ข้อแม้: คุณจะต้องระมัดระวังนิดถ้าใช้จับasyncแต่ซิงค์ดี - คือข้างต้นเป็นดี)

ตัวเลือกอื่นคือการเขียนวิธีการขยาย:

public static void Invoke(this Control control, Action action)
{
    control.Invoke((Delegate)action);
}

แล้ว:

this.Invoke(delegate { this.Text = "hi"; });
// or since we are using C# 3.0
this.Invoke(() => { this.Text = "hi"; });

แน่นอนคุณสามารถทำเช่นเดียวกันกับBeginInvoke:

public static void BeginInvoke(this Control control, Action action)
{
    control.BeginInvoke((Delegate)action);
}

หากคุณไม่สามารถใช้ C # 3.0 ได้คุณสามารถทำเช่นนี้ได้ด้วยวิธีปกติเช่นสมมุติในFormคลาสฐาน


ฉันจะส่งพารามิเตอร์ไปยังโซลูชันแรกของคุณในคำตอบนี้ได้อย่างไร ฉันหมายถึงโซลูชันนี้: control.Invoke ((MethodInvoker)) มอบสิทธิ์ {this.Text = "Hi";});
uzay95

1
เหตุใดวิธีการส่วนขยายจึงถูกเรียกใช้โดยไม่ต้องทำการส่งข้อมูลไปที่การดำเนินการอย่างชัดเจน
P.Brian.Mackey

เพราะคอมไพเลอร์สามารถอนุมานได้ว่าจากการใช้งาน
RoboJ1M

1
มันเหมือนกับที่คุณสามารถทำได้Form.Load += Loader()แทนที่จะเป็นแบบเก่าForm.Load += new EventHandler(Loader())
RoboJ1M

49

ที่จริงแล้วคุณไม่จำเป็นต้องใช้คำสำคัญตัวแทน เพียงผ่านแลมบ์ดาเป็นพารามิเตอร์:

control.Invoke((MethodInvoker)(() => {this.Text = "Hi"; }));


13

คุณต้องสร้างประเภทตัวแทน คำหลัก 'ตัวแทน' ในการสร้างวิธีการที่ไม่ระบุชื่อนั้นทำให้เข้าใจผิดเล็กน้อย คุณไม่ได้สร้างตัวแทนที่ไม่ระบุชื่อ แต่เป็นวิธีที่ไม่ระบุชื่อ วิธีการที่คุณสร้างสามารถใช้ในการมอบสิทธิ์ แบบนี้:

myControl.Invoke(new MethodInvoker(delegate() { (MyMethod(this, new MyEventArgs(someParameter)); }));

8

เพื่อประโยชน์ของความครบถ้วนสมบูรณ์สิ่งนี้สามารถทำได้ผ่านการรวมกันของวิธีการดำเนินการ / วิธีการที่ไม่ระบุชื่อ:

//Process is a method, invoked as a method group
Dispatcher.Current.BeginInvoke((Action) Process);
//or use an anonymous method
Dispatcher.Current.BeginInvoke((Action)delegate => {
  SomeFunc();
  SomeOtherFunc();
});

Invoke((Action) Process);เป็นคำตอบที่ดีที่สุดขอบคุณ!
Jinjinov

5

ฉันมีปัญหากับคำแนะนำอื่น ๆ เพราะบางครั้งฉันต้องการคืนค่าจากวิธีการของฉัน หากคุณพยายามใช้ MethodInvoker ด้วยค่าส่งคืนดูเหมือนว่าจะไม่ชอบ ดังนั้นวิธีที่ฉันใช้เป็นเช่นนี้ (ดีใจมากที่ได้ยินวิธีทำให้สั้นกระชับยิ่งขึ้น - ฉันใช้ c # .net 2.0):

    // Create delegates for the different return types needed.
    private delegate void VoidDelegate();
    private delegate Boolean ReturnBooleanDelegate();
    private delegate Hashtable ReturnHashtableDelegate();

    // Now use the delegates and the delegate() keyword to create 
    // an anonymous method as required

    // Here a case where there's no value returned:
    public void SetTitle(string title)
    {
        myWindow.Invoke(new VoidDelegate(delegate()
        {
            myWindow.Text = title;
        }));
    }

    // Here's an example of a value being returned
    public Hashtable CurrentlyLoadedDocs()
    {
        return (Hashtable)myWindow.Invoke(new ReturnHashtableDelegate(delegate()
        {
            return myWindow.CurrentlyLoadedDocs;
        }));
    }

1

ฉันชอบใช้ Action แทน MethodInvoker มันสั้นกว่าและดูสะอาดกว่า

Invoke((Action)(() => {
    DoSomething();
}));

// OR

Invoke((Action)delegate {
    DoSomething();
});

เช่น.

// Thread-safe update on a form control
public void DisplayResult(string text){
    if (txtResult.InvokeRequired){
        txtResult.Invoke((Action)delegate {
            DisplayResult(text);
        });
        return;
    }

    txtResult.Text += text + "\r\n";
}

0

ฉันไม่เคยเข้าใจว่าทำไมสิ่งนี้ถึงสร้างความแตกต่างให้กับคอมไพเลอร์ แต่ก็เพียงพอแล้ว

public static class ControlExtensions
{
    public static void Invoke(this Control control, Action action)
    {
        control.Invoke(action);
    }
}

โบนัส: เพิ่มการจัดการข้อผิดพลาดเนื่องจากเป็นไปได้ว่าหากคุณกำลังใช้ Control.Invokeจากเธรดพื้นหลังคุณกำลังอัปเดตสถานะข้อความ / ความคืบหน้า / เปิดใช้งานการควบคุมและไม่สนใจว่าการควบคุมนั้นถูกกำจัดไปแล้ว

public static class ControlExtensions
{
    public static void Invoke(this Control control, Action action)
    {
        try
        {
            if (!control.IsDisposed) control.Invoke(action);
        }
        catch (ObjectDisposedException) { }
    }
}
โดยการใช้ไซต์ของเรา หมายความว่าคุณได้อ่านและทำความเข้าใจนโยบายคุกกี้และนโยบายความเป็นส่วนตัวของเราแล้ว
Licensed under cc by-sa 3.0 with attribution required.