"จะบล็อกการไหลของรหัสได้อย่างไรจนกว่าเหตุการณ์จะเริ่มทำงาน"
วิธีการของคุณผิด การขับเคลื่อนเหตุการณ์ไม่ได้หมายถึงการบล็อกและรอเหตุการณ์ คุณไม่เคยรออย่างน้อยคุณพยายามอย่างหนักเพื่อหลีกเลี่ยง การรอคอยเป็นการสิ้นเปลืองทรัพยากรบล็อกเธรดและอาจแนะนำความเสี่ยงของการหยุดชะงักหรือเธรดซอมบี้ (ในกรณีที่ไม่มีการเพิ่มสัญญาณปล่อย)
ควรชัดเจนว่าการบล็อกเธรดเพื่อรอเหตุการณ์เป็นรูปแบบการต่อต้านเนื่องจากขัดแย้งกับแนวคิดของเหตุการณ์
โดยทั่วไปคุณมีสองตัวเลือก (ทันสมัย): ใช้ API แบบอะซิงโครนัสหรือ API ที่ขับเคลื่อนด้วยเหตุการณ์ เนื่องจากคุณไม่ต้องการใช้ API แบบอะซิงโครนัสคุณจึงเหลือ API ที่ขับเคลื่อนด้วยเหตุการณ์
กุญแจสำคัญของ API ที่ขับเคลื่อนด้วยเหตุการณ์คือแทนที่จะบังคับให้ผู้โทรรอผลลัพธ์หรือแบบสำรวจความคิดเห็นเพื่อให้ผลลัพธ์คุณให้ผู้โทรดำเนินการต่อและส่งการแจ้งเตือนเมื่อผลลัพธ์พร้อมหรือการดำเนินการเสร็จสิ้น ในขณะเดียวกันผู้เรียกสามารถดำเนินการอื่น ๆ
เมื่อมองปัญหาจากมุมมองของเธรดเธรด API ที่ขับเคลื่อนด้วยเหตุการณ์จะอนุญาตให้เธรดการโทรเช่นเธรด UI ซึ่งดำเนินการตัวจัดการเหตุการณ์ของปุ่มให้เป็นอิสระในการจัดการต่อไปเช่นการดำเนินการอื่น ๆ ที่เกี่ยวข้องกับ UI เช่นการแสดงองค์ประกอบ UI หรือจัดการอินพุตของผู้ใช้เช่นการเคลื่อนไหวของเมาส์และการกดปุ่ม API ที่ขับเคลื่อนด้วยเหตุการณ์มีเอฟเฟกต์หรือเป้าหมายเหมือน API แบบอะซิงโครนัสแม้ว่าจะสะดวกน้อยกว่ามาก
เนื่องจากคุณไม่ได้ให้รายละเอียดที่เพียงพอเกี่ยวกับสิ่งที่คุณพยายามทำสิ่งที่Utility.PickPoint()
ทำอยู่จริงและผลลัพธ์ของงานคืออะไรหรือเหตุใดผู้ใช้ต้องคลิกที่ `กริดฉันจึงไม่สามารถเสนอทางออกที่ดีกว่าให้คุณได้ . ฉันสามารถเสนอรูปแบบทั่วไปของวิธีการใช้ความต้องการของคุณ
โฟลว์หรือเป้าหมายของคุณแบ่งออกเป็นสองขั้นตอนอย่างชัดเจนเพื่อให้เป็นลำดับของการปฏิบัติงาน:
- ดำเนินการการทำงาน 1 เมื่อผู้ใช้คลิกปุ่ม
- ดำเนินการการดำเนินการ 2 (ดำเนินการต่อ / ดำเนินการจนเสร็จสิ้น 1) เมื่อผู้ใช้คลิกที่
Grid
มีข้อ จำกัด อย่างน้อยสองข้อ:
- ทางเลือก: ลำดับจะต้องเสร็จสิ้นก่อนที่ไคลเอ็นต์ API จะได้รับอนุญาตให้ทำซ้ำได้ ลำดับเสร็จสมบูรณ์เมื่อการดำเนินงาน 2 เสร็จสิ้นลง
- การดำเนินการ 1 จะถูกดำเนินการเสมอก่อนการดำเนินการ 2 การดำเนินการ 1 เริ่มต้นลำดับ
- การดำเนินการ 1 จะต้องเสร็จสิ้นก่อนที่ไคลเอ็นต์ API จะได้รับอนุญาตให้ดำเนินการ 2
สิ่งนี้ต้องการการแจ้งเตือน (เหตุการณ์) สองรายการสำหรับลูกค้าของ API เพื่ออนุญาตการไม่โต้ตอบ:
- การดำเนินการ 1 เสร็จสมบูรณ์ (หรือจำเป็นต้องมีการโต้ตอบ)
- การดำเนินการ 2 (หรือเป้าหมาย) เสร็จสมบูรณ์
คุณควรปล่อยให้ API ของคุณใช้พฤติกรรมและข้อ จำกัด นี้โดยการเปิดเผยวิธีสาธารณะสองวิธีและกิจกรรมสาธารณะสองเหตุการณ์
เนื่องจากการใช้งานนี้อนุญาตให้เรียก API (ไม่เกิดขึ้นพร้อมกัน) หนึ่งครั้งเท่านั้นจึงขอแนะนำให้เปิดเผยIsBusy
คุณสมบัติเพื่อระบุลำดับการทำงาน การทำเช่นนี้ช่วยให้การสำรวจสถานะปัจจุบันก่อนเริ่มลำดับใหม่แม้ว่าจะแนะนำให้รอให้เหตุการณ์ที่เสร็จสิ้นแล้วเพื่อเรียกใช้งานตามลำดับ
Implement / refactor Utility API
Utility.cs
class Utility
{
public event EventHandler InitializePickPointCompleted;
public event EventHandler<PickPointCompletedEventArgs> PickPointCompleted;
public bool IsBusy { get; set; }
private bool IsPickPointInitialized { get; set; }
// The prefix 'Begin' signals the caller or client of the API,
// that he also has to end the sequence explicitly
public void BeginPickPoint(param)
{
// Implement constraint 1
if (this.IsBusy)
{
// Alternatively just return or use Try-do pattern
throw new InvalidOperationException("BeginPickPoint is already executing. Call EndPickPoint before starting another sequence.");
}
// Set the flag that a current sequence is in progress
this.IsBusy = true;
// Execute operation until caller interaction is required.
// Execute in background thread to allow API caller to proceed with execution.
Task.Run(() => StartOperationNonBlocking(param));
}
public void EndPickPoint(param)
{
// Implement constraint 2 and 3
if (!this.IsPickPointInitialized)
{
// Alternatively just return or use Try-do pattern
throw new InvalidOperationException("BeginPickPoint must have completed execution before calling EndPickPoint.");
}
// Execute operation until caller interaction is required.
// Execute in background thread to allow API caller to proceed with execution.
Task.Run(() => CompleteOperationNonBlocking(param));
}
private void StartOperationNonBlocking(param)
{
... // Do something
// Flag the completion of the first step of the sequence (to guarantee constraint 2)
this.IsPickPointInitialized = true;
// Request caller interaction to kick off EndPickPoint() execution
OnInitializePickPointCompleted();
}
private void CompleteOperationNonBlocking(param)
{
// Execute goal and get the result of the completed task
Point result = ExecuteGoal();
// Reset API sequence (allow next client invocation)
this.IsBusy = false;
this.IsPickPointInitialized = false;
// Notify caller that execution has completed and the result is available
OnPickPointCompleted(result);
}
private void OnInitializePickPointCompleted()
{
// Set the result of the task
this.InitializePickPointCompleted?.Invoke(this, EventArgs.Empty);
}
private void OnPickPointCompleted(Point result)
{
// Set the result of the task
this.PickPointCompleted?.Invoke(this, new PickPointCompletedEventArgs(result));
}
}
PickPointCompletedEventArgs.cs
class PickPointCompletedEventArgs : AsyncCompletedEventArgs
{
public Point Result { get; }
public PickPointCompletedEventArgs(Point result)
{
this.Result = result;
}
}
ใช้ API
MainWindow.xaml.cs
partial class MainWindow : Window
{
private Utility Api { get; set; }
public MainWindow()
{
InitializeComponent();
this.Api = new Utility();
}
private void StartPickPoint_OnButtonClick(object sender, RoutedEventArgs e)
{
this.Api.InitializePickPointCompleted += RequestUserInput_OnInitializePickPointCompleted;
// Invoke API and continue to do something until the first step has completed.
// This is possible because the API will execute the operation on a background thread.
this.Api.BeginPickPoint();
}
private void RequestUserInput_OnInitializePickPointCompleted(object sender, EventArgs e)
{
// Cleanup
this.Api.InitializePickPointCompleted -= RequestUserInput_OnInitializePickPointCompleted;
// Communicate to the UI user that you are waiting for him to click on the screen
// e.g. by showing a Popup, dimming the screen or showing a dialog.
// Once the input is received the input event handler will invoke the API to complete the goal
MessageBox.Show("Please click the screen");
}
private void FinishPickPoint_OnGridMouseLeftButtonUp(object sender, MouseButtonEventArgs e)
{
this.Api.PickPointCompleted += ShowPoint_OnPickPointCompleted;
// Invoke API to complete the goal
// and continue to do something until the last step has completed
this.Api.EndPickPoint();
}
private void ShowPoint_OnPickPointCompleted(object sender, PickPointCompletedEventArgs e)
{
// Cleanup
this.Api.PickPointCompleted -= ShowPoint_OnPickPointCompleted;
// Get the result from the PickPointCompletedEventArgs instance
Point point = e.Result;
// Handle the result
MessageBox.Show(point.ToString());
}
}
MainWindow.xaml
<Window>
<Grid MouseLeftButtonUp="FinishPickPoint_OnGridMouseLeftButtonUp">
<Button Click="StartPickPoint_OnButtonClick" />
</Grid>
</Window>
หมายเหตุ
กิจกรรมที่เพิ่มขึ้นในเธรดพื้นหลังจะดำเนินการจัดการของพวกเขาในหัวข้อเดียวกัน การเข้าถึงDispatcherObject
อิลีเมนต์ UI จากตัวจัดการซึ่งถูกเรียกใช้งานบนเธรดเบื้องหลังต้องการการดำเนินการที่สำคัญเพื่อจัดคิวการDispatcher
ใช้Dispatcher.Invoke
หรือDispatcher.InvokeAsync
เพื่อหลีกเลี่ยงข้อยกเว้นข้ามเธรด
อ่านคำพูดเกี่ยวกับDispatcherObject
เพื่อเรียนรู้เพิ่มเติมเกี่ยวกับปรากฏการณ์นี้ที่เรียกว่าความสัมพันธ์ดิสแพตเชอร์หรือความสัมพันธ์ของเธรด
เพื่อความสะดวกในการใช้ API ฉันขอแนะนำให้จัดกิจกรรมทั้งหมดตามบริบทดั้งเดิมของผู้โทรด้วยการจับภาพและใช้ผู้โทรSynchronizationContext
หรือโดยใช้AsyncOperation
(หรือAsyncOperationManager
)
ตัวอย่างข้างต้นสามารถปรับปรุงได้อย่างง่ายดายโดยการยกเลิก (แนะนำ) เช่นโดยการเปิดเผยCancel()
วิธีเช่นPickPointCancel()
และการรายงานความคืบหน้า (ควรใช้Progress<T>
)
ความคิด - ตอบความคิดเห็นของคุณ
เนื่องจากคุณกำลังมาหาฉันเพื่อหาทางออกการบล็อกที่ "ดีกว่า" ให้ฉันเป็นตัวอย่างของแอปพลิเคชันคอนโซลฉันรู้สึกว่าคุณเชื่อว่าการรับรู้หรือมุมมองของคุณผิดทั้งหมด
ลองพิจารณาแอปพลิเคชั่นคอนโซลพร้อมโค้ดสองบรรทัดในตัว
var str = Console.ReadLine();
Console.WriteLine(str);
จะเกิดอะไรขึ้นเมื่อคุณเรียกใช้แอปพลิเคชันในโหมดแก้ไขข้อบกพร่อง มันจะหยุดที่บรรทัดแรกของรหัสและบังคับให้คุณป้อนค่าใน Console UI จากนั้นหลังจากที่คุณป้อนบางสิ่งและกด Enter มันจะดำเนินการบรรทัดถัดไปและพิมพ์สิ่งที่คุณป้อนจริง ๆ ฉันคิดเกี่ยวกับพฤติกรรมเดียวกันทั้งหมด แต่ในแอปพลิเคชัน WPF "
แอปพลิเคชันคอนโซลเป็นสิ่งที่แตกต่างอย่างสิ้นเชิง แนวคิดของเธรดแตกต่างกันเล็กน้อย แอปพลิเคชันคอนโซลไม่มี GUI เพียงแค่อินพุต / เอาต์พุต / สตรีมข้อผิดพลาด คุณไม่สามารถเปรียบเทียบสถาปัตยกรรมของแอปพลิเคชันคอนโซลกับแอปพลิเคชัน GUI ที่มีรูปแบบต่างกันได้ สิ่งนี้ใช้ไม่ได้ คุณต้องเข้าใจและยอมรับสิ่งนี้จริงๆ
นอกจากนี้ยังไม่ได้รับการหลอกโดยลักษณะ คุณรู้หรือไม่ว่าเกิดอะไรขึ้นภายใน Console.ReadLine
? มีการใช้งานอย่างไร? มันบล็อกเธรดหลักหรือไม่และจะอ่านอินพุตแบบขนานหรือไม่ หรือว่าเป็นแค่การสำรวจความคิดเห็น?
นี่คือการดำเนินการเดิมของConsole.ReadLine
:
public virtual String ReadLine()
{
StringBuilder sb = new StringBuilder();
while (true)
{
int ch = Read();
if (ch == -1)
break;
if (ch == '\r' || ch == '\n')
{
if (ch == '\r' && Peek() == '\n')
Read();
return sb.ToString();
}
sb.Append((char)ch);
}
if (sb.Length > 0)
return sb.ToString();
return null;
}
อย่างที่คุณเห็นมันเป็นเรื่องง่าย ซิงโครการดำเนินงาน มันสำรวจความคิดเห็นของผู้ใช้ในการวนซ้ำ "ไม่มีที่สิ้นสุด" ไม่มีบล็อกเวทมนต์และดำเนินการต่อ
WPF สร้างขึ้นรอบ ๆ เธรดการแสดงผลและเธรด UI เธรดเหล่านั้นหมุนอยู่เสมอเพื่อสื่อสารกับระบบปฏิบัติการเช่นการจัดการอินพุตของผู้ใช้ - การเก็บแอปพลิเคชันตอบสนองได้ คุณไม่ต้องการหยุด / บล็อกเธรดนี้เนื่องจากจะทำให้เฟรมเวิร์กไม่ทำงานพื้นหลังที่สำคัญเช่นการตอบสนองต่อเหตุการณ์เมาส์ - คุณไม่ต้องการให้เมาส์ค้าง:
กำลังรอ = การบล็อกเธรด = ไม่ตอบสนอง = ไม่ดี UX = ผู้ใช้ / ลูกค้ารำคาญ = ปัญหาในสำนักงาน
บางครั้งโฟลว์แอปพลิเคชันจำเป็นต้องรออินพุตหรือรูทีนเพื่อให้สมบูรณ์ แต่เราไม่ต้องการบล็อกเธรดหลัก
นั่นเป็นสาเหตุที่ผู้คนคิดค้นรูปแบบการเขียนโปรแกรมแบบอะซิงโครนัสที่ซับซ้อนเพื่อรอโดยไม่ปิดกั้นเธรดหลักและไม่บังคับให้นักพัฒนาเขียนรหัสมัลติเธรดที่ซับซ้อนและผิดพลาด
เฟรมเวิร์กแอปพลิเคชันที่ทันสมัยทุกข้อเสนอการดำเนินการแบบอะซิงโครนัสหรือรูปแบบการเขียนโปรแกรมแบบอะซิงโครนัสเพื่อให้การพัฒนาโค้ดง่ายและมีประสิทธิภาพ
ความจริงที่ว่าคุณกำลังพยายามอย่างหนักที่จะต่อต้านรูปแบบการเขียนโปรแกรมแบบอะซิงโครนัสแสดงให้เห็นว่าฉันขาดความเข้าใจ นักพัฒนาสมัยใหม่ทุกคนชอบ API แบบอะซิงโครนัสมากกว่าแบบซิงโครนัส ไม่มีนักพัฒนาที่จริงจังที่จะใช้await
คำหลักหรือประกาศวิธีการของเขาasync
คำหลักหรือจะประกาศวิธีการของเขาไม่มีใคร คุณเป็นคนแรกที่ฉันพบผู้ที่บ่นเกี่ยวกับ API แบบอะซิงโครนัสและผู้ที่พบว่าพวกเขาไม่สะดวกที่จะใช้
ถ้าฉันจะตรวจสอบเฟรมเวิร์กของคุณซึ่งมีเป้าหมายเพื่อแก้ไขปัญหาที่เกี่ยวข้องกับ UI หรือทำให้งานที่เกี่ยวข้องกับ UI นั้นง่ายขึ้นฉันก็คาดหวังว่ามันจะเป็นแบบอะซิงโครนัส - ไปตลอดทาง
API ที่เกี่ยวข้องกับ UI ซึ่งไม่อะซิงโครนัสจะสูญเปล่าเนื่องจากจะทำให้รูปแบบการเขียนโปรแกรมของฉันซับซ้อนยิ่งขึ้นดังนั้นโค้ดของฉันซึ่งมีแนวโน้มที่จะเกิดข้อผิดพลาดได้ง่ายขึ้นและยากต่อการดูแลรักษา
มุมมองที่แตกต่าง: เมื่อคุณยอมรับว่าการรอคอยบล็อกเธรด UI กำลังสร้างประสบการณ์การใช้งานที่ไม่ดีและไม่พึงประสงค์เนื่องจาก UI จะหยุดจนกว่าการรอจะสิ้นสุดลงเมื่อคุณตระหนักถึงสิ่งนี้แล้วทำไมคุณถึงเสนอ API หรือปลั๊กอิน สนับสนุนให้นักพัฒนาทำสิ่งนี้ - ใช้การรอ?
คุณไม่ทราบว่าปลั๊กอินของบุคคลที่สามจะทำอะไรและจะใช้เวลานานเท่าใดในการรูทีนจนกว่าจะเสร็จสมบูรณ์ นี่เป็นเพียงการออกแบบ API ที่ไม่ดี เมื่อ API ของคุณทำงานบนเธรด UI ผู้โทรของ API ของคุณจะต้องสามารถโทรออกโดยไม่บล็อกได้
หากคุณปฏิเสธโซลูชันราคาถูกหรือสง่างามเพียงอย่างเดียวให้ใช้วิธีการจัดกิจกรรมตามที่แสดงในตัวอย่างของฉัน
มันทำในสิ่งที่คุณต้องการ: เริ่มกิจวัตรประจำวัน - รอการป้อนข้อมูลของผู้ใช้ - ดำเนินการต่อ - บรรลุเป้าหมาย
ฉันพยายามหลายครั้งเพื่ออธิบายว่าทำไมการรอ / บล็อกจึงเป็นการออกแบบแอปพลิเคชันที่ไม่ดี อีกครั้งคุณไม่สามารถเปรียบเทียบ UI ของคอนโซลกับ UI แบบกราฟิกที่หลากหลายเช่นการจัดการอินพุตอย่างเดียวมีความหลากหลายมากกว่าที่ซับซ้อนเพียงแค่รับฟังอินพุตสตรีม ฉันไม่รู้ระดับประสบการณ์ของคุณและตำแหน่งที่คุณเริ่ม แต่คุณควรเริ่มต้นที่จะยอมรับรูปแบบการเขียนโปรแกรมแบบอะซิงโครนัส ฉันไม่รู้เหตุผลที่คุณพยายามหลีกเลี่ยง แต่มันไม่ฉลาดเลย
รูปแบบการเขียนโปรแกรมแบบอะซิงโครนัสวันนี้มีการใช้งานทุกที่ในทุกแพลตฟอร์มคอมไพเลอร์ทุกสภาพแวดล้อมเบราว์เซอร์เซิร์ฟเวอร์เดสก์ท็อปฐานข้อมูล - ทุกที่ โมเดลที่ขับเคลื่อนด้วยเหตุการณ์อนุญาตให้บรรลุเป้าหมายเดียวกัน แต่จะสะดวกกว่าในการใช้ (สมัคร / ยกเลิกการเป็นสมาชิก / จากเหตุการณ์อ่านเอกสาร (เมื่อมีเอกสาร) เพื่อเรียนรู้เกี่ยวกับเหตุการณ์) โดยอาศัยเธรดพื้นหลัง Event-driven เป็นแบบเก่าและควรใช้เฉพาะเมื่อไลบรารีแบบอะซิงโครนัสไม่พร้อมใช้งานหรือไม่สามารถใช้ได้
จากหมายเหตุด้านข้าง:. Framwork .NET (. NET Standard) นำเสนอTaskCompletionSource
(ท่ามกลางวัตถุประสงค์อื่น ๆ ) เพื่อให้วิธีง่ายๆในการแปลง API ที่มีอยู่เดิมที่มีอยู่ให้เป็น API แบบอะซิงโครนัส
"ฉันเห็นพฤติกรรมที่แน่นอนใน Autodesk Revit"
พฤติกรรม (สิ่งที่คุณสัมผัสหรือสังเกตเห็น) แตกต่างจากการใช้งานประสบการณ์นี้มาก สองสิ่งที่แตกต่าง Autodesk ของคุณน่าจะใช้ไลบรารีแบบอะซิงโครนัสหรือคุณสมบัติภาษาหรือกลไกการเธรดอื่น ๆ และยังเกี่ยวข้องกับบริบทด้วย เมื่อวิธีที่อยู่ในใจของคุณกำลังดำเนินการบนเธรดพื้นหลังนักพัฒนาอาจเลือกที่จะบล็อกเธรดนี้ เขามีเหตุผลที่ดีที่จะทำสิ่งนี้หรือเพียงแค่เลือกการออกแบบที่ไม่ดี คุณอยู่ในเส้นทางที่ไม่ถูกต้อง;) การบล็อกไม่ดี
(ซอร์สรหัส Autodesk เป็นโอเพ่นซอร์สหรือไม่หรือคุณรู้ได้อย่างไรว่ามีการนำไปใช้อย่างไร)
ฉันไม่ต้องการที่จะรุกรานคุณโปรดเชื่อฉัน แต่โปรดพิจารณาใหม่เพื่อใช้ API แบบอะซิงโครนัสของคุณ มีอยู่ในหัวของคุณเท่านั้นที่นักพัฒนาซอฟต์แวร์ไม่ต้องการใช้ async / คอย เห็นได้ชัดว่าคุณมีความคิดที่ผิด และลืมเกี่ยวกับอาร์กิวเมนต์แอปพลิเคชันคอนโซลนั่น - มันไร้สาระ;)
API ที่เกี่ยวข้องกับ UI ต้อง ใช้ async / รอเมื่อเป็นไปได้ มิฉะนั้นคุณจะปล่อยให้งานทั้งหมดเขียนโค้ดที่ไม่บล็อกไปยังลูกค้าของ API ของคุณ คุณจะบังคับให้ฉันตัดทุกการเรียก API ของคุณลงในเธรดพื้นหลัง หรือใช้การจัดการเหตุการณ์ที่สะดวกสบายน้อยลง เชื่อฉัน - นักพัฒนาทุกคนต่างตกแต่งสมาชิกของเขาasync
มากกว่าทำกิจกรรมจัดการ ทุกครั้งที่คุณใช้เหตุการณ์คุณอาจเสี่ยงต่อการรั่วไหลของหน่วยความจำ - ขึ้นอยู่กับสถานการณ์บางอย่าง แต่ความเสี่ยงนั้นเป็นจริงและไม่เกิดขึ้นยากเมื่อตั้งโปรแกรม
ฉันหวังว่าคุณจะเข้าใจว่าเพราะเหตุใดการบล็อกจึงไม่ดี ฉันหวังว่าคุณจะตัดสินใจใช้ async / รอการเขียน API แบบอะซิงโครนัสที่ทันสมัย อย่างไรก็ตามฉันแสดงให้คุณเห็นวิธีที่ธรรมดามากในการรอการไม่บล็อกการใช้กิจกรรมแม้ว่าฉันขอให้คุณใช้ async / คอย
"API จะช่วยให้โปรแกรมเมอร์สามารถเข้าถึง UI และอื่น ๆ ได้ตอนนี้สมมติว่าโปรแกรมเมอร์ต้องการพัฒนา Add-in ที่เมื่อคลิกปุ่มผู้ใช้ขั้นสุดท้ายจะถูกขอให้เลือกจุดใน UI"
หากคุณไม่ต้องการอนุญาตให้ปลั๊กอินเข้าถึงองค์ประกอบ UI ได้โดยตรงคุณควรจัดให้มีอินเทอร์เฟซสำหรับมอบหมายกิจกรรมหรือเปิดเผยส่วนประกอบภายในผ่านวัตถุที่เป็นนามธรรม
API ภายในจะสมัครสมาชิกกิจกรรม UI ในนามของ Add-in จากนั้นมอบหมายกิจกรรมโดยการเปิดเผยเหตุการณ์ "wrapper" ที่สอดคล้องกับไคลเอ็นต์ API API ของคุณต้องมีตะขอบางอย่างที่ Add-in สามารถเชื่อมต่อเพื่อเข้าถึงส่วนประกอบแอปพลิเคชันที่เฉพาะเจาะจง API ปลั๊กอินทำหน้าที่เหมือนอะแดปเตอร์หรือซุ้มเพื่อให้เข้าถึงภายนอกจากภายใน
เพื่อให้ระดับความเหงา
ดูที่ Visual Studio จัดการปลั๊กอินหรือช่วยให้เราสามารถใช้งานได้ แกล้งทำเป็นว่าคุณต้องการเขียนปลั๊กอินสำหรับ Visual Studio และทำวิจัยเกี่ยวกับวิธีการทำเช่นนี้ คุณจะรู้ว่า Visual Studio แสดงถึง internals ผ่านทางอินเตอร์เฟสหรือ API EG คุณสามารถจัดการแก้ไขรหัสหรือได้รับข้อมูลเกี่ยวกับเนื้อหาของบรรณาธิการโดยไม่จริงการเข้าถึงมัน
Aync/Await
วิธีการเกี่ยวกับการดำเนินการ A และการบันทึกการดำเนินการของรัฐในขณะนี้คุณต้องการให้ผู้ใช้คลิกกริด .. ดังนั้นหากผู้ใช้คลิกกริดคุณตรวจสอบสถานะถ้าเป็นจริงจากนั้นดำเนินการอื่น