سؤال

نت 4.0 بيتا 2 لديه قدَّم ال يمكن ملاحظته و IObserver واجهات.

ما هي المزايا مقارنة بأحداث .NET الكلاسيكية؟ألا يحل هذا نفس المشكلة؟

هل كانت مفيدة؟

المحلول

يمكنك استخدام IObservable كحدث، واستبدال التعليمات البرمجية التي تعرض الأحداث بخصائص من النوع IObservable، ولكن هذا ليس هو الهدف حقًا.

هناك شيئان مهمان يجب فهمهما حول IObservable:

  1. فهو يوحد مفهومين لم نعرف كيف نوحدهما من قبل:العمليات غير المتزامنة (التي تُرجع عادةً قيمة واحدة) والأحداث (التي تستمر عادةً إلى الأبد).

  2. إنه قابل للتأليف.على عكس أحداث CLR، أو IAsyncResult، أو INotifyCollectionChanged، فإنه يسمح لنا ببناء أحداث محددة من الأحداث العامة والعمليات غير المتزامنة.

إليك مثال واجهته في العمل بعد ظهر هذا اليوم.

توجد في Silverlight بعض التأثيرات التي يمكنك تطبيقها على عنصر تحكم الصورة والتي لا يمكن تطبيقها على عنصر تحكم عادي.للتغلب على هذه القيود عند تغيير محتوى عنصر التحكم، يمكنني الانتظار حتى يتم تحديث مظهره المرئي والتقاط لقطة شاشة له.ثم أريد إخفاء تمثيلها المرئي واستبدالها باللقطة وتطبيق التأثيرات المرئية على الصورة.يمكنني الآن تطبيق تأثيرات الصورة على عنصر التحكم (بافتراض أنه ليس تفاعليًا).

سيكون هذا البرنامج تافهًا لولا أنه يجب أن يكون غير متزامن.يجب أن أنتظر حتى تكتمل عمليتان متتاليتان غير متزامنتين قبل أن أتمكن من تطبيق التأثيرات على الصورة:

  1. تم تغيير محتوى عنصر التحكم
  2. يتم تحديث المظهر المرئي لعنصر التحكم

إليك كيفية حل هذه المشكلة باستخدام Rx:

// A content control is a control that displays content.  That content can be
// anything at all like a string or another control.  Every content control contains
// another control: a ContentPresenter.  The ContentPresenter's job is to generate
// a visual representation of the Content property. For example, if the Content property
// of the ContentControl is a string, the ContentPresenter creates a TextBlock and inserts
// the string into it.  On the other hand if the Content property is another control the 
// ContentPresenter just inserts it into the visual tree directly.
public class MyContentControl : ContentControl
{
   // A subject implements both IObservable and IObserver.  When IObserver methods
   // are called, it forwards those calls to all of its listeners.
   // As a result it has roughly the same semantics as an event that we can "raise."
   private Subject<object> contentChanged = new Subject<object>();

   // This is a reference to the ContentPresenter in the ContentControl's template
   private ContentPresenter contentPresenter; 

   // This is a reference to the Image control within ContentControl's template.  It is displayed on top of the ContentPresenter and has a cool blur effect applied to it.
   private Image contentImageControl; 

   public MyContentControl()
   {
      // Using Rx we can create specific events from general events.
      // In this case I want to create a specific event ("contentImageChanged") which
      // gives me exactly the data I need to respond and update the UI.
      var contentImageChanged = 
         // get the content from the content changed event
         from content in contentChanged
         where content != null
         // Wait for the ContentPresenter's visual representation to update.
         // ContentPresenter is data bound to the Content property, so it will
         // update momentarily.
         from _ in contentPresenter.GetLayoutUpdated().Take(1)
         select new WritableBitmap(contentPresenter, new TranslateTransform());

      contentImageChanged.Subscribe(
         contentImage => 
         {
            // Hide the content presenter now that we've taken a screen shot              
            contentPresenter.Visibility = Visibility.Collapsed; 

            // Set the image source of the image control to the snapshot
            contentImageControl.ImageSource = contentImage;
         });
   }

   // This method is invoked when the Content property is changed.
   protected override OnContentChanged(object oldContent, object newContent)
   {
      // show the content presenter before taking screenshot
      contentPresenter.Visibility = Visibility.Visible;  

      // raise the content changed "event"
      contentChanged.OnNext(newContent);   

      base.OnContentChanged(oldContent, newContent);
   }
}

هذا المثال بسيط بشكل خاص نظرًا لوجود عمليتين متتاليتين فقط للتسلسل.حتى في هذا المثال البسيط يمكننا أن نرى أن Rx يضيف قيمة.بدونها كنت سأضطر إلى استخدام متغيرات الحالة للتأكد من إطلاق الأحداث بترتيب معين.كان علي أيضًا أن أكتب بعض التعليمات البرمجية القبيحة جدًا لفصلها بشكل صريح عن حدث LayoutUpdated.

لقد تدربنا على التفكير في الأحداث باعتبارها أشياء بسيطة تعتمد على المدخلات ("تمرير الماوس"، "النقر بالماوس"، "تشغيل المفاتيح"، وما إلى ذلك).ومع ذلك، لا يوجد سبب يمنع أن تكون الأحداث معقدة جدًا ومحددة لتطبيقك ("GoogleMsdnMashupStockDataArrived"، و"DragStarting"، و"ImageContentChanged").عندما تقوم بتنظيم برامجك بهذه الطريقة (قم بإنشاء الحدث الذي أحتاجه بالضبط وثم استجب لها عن طريق تغيير الحالة) ستجد أن لديهم عددًا أقل من أخطاء الحالة، وأصبحوا أكثر ترتيبًا، وأكثر وصفًا لذاتهم تمامًا.

فهمتها؟:-)

نصائح أخرى

لست متأكدًا من المزايا، ولكني أرى الاختلافات التالية مع أحداث .NET الكلاسيكية:

إخطارات الخطأ

قد تتطلب الأحداث الكلاسيكية حدثًا منفصلاً لهذا الغرض، أو EventArgs فئة مع Error الممتلكات التي تحتاج إلى التحقق.

إشعار نهاية الإخطارات

تتطلب الأحداث الكلاسيكية حدثًا منفصلاً لهذا أو EventArgs فئة مع أ Final الممتلكات التي تحتاج إلى التحقق.

إنه مجرد امتداد لنموذج البرمجة القائم على الحدث.تقوم بإنشاء شيء ينفذ IObserver، وأنت تقول بشكل أساسي "هذا ما أريد أن يحدث عندما يتغير شيء ما في المجموعة".بهذه الطريقة، إنه مجرد توحيد لما كنا نفعله جميعًا بالأحداث.

إنهم يدفعون بها كما لو كانت تحولًا كبيرًا مقارنة بنمط IEnumerable.IEnumerable هو "سحب"، بينما IObservable هو "دفع".

الميزة الوحيدة التي أراها في الأحداث المباشرة هي أنها واجهة موحدة.أرى تداخلًا كبيرًا مع ObservableCollection هنا (و INotifyCollectionChanged).ربما يحاولون اعتماد شعار PERL مع .NET:"هناك أكثر من طريقة للقيام بذلك".

يجب عليك بالتأكيد مشاهدة ورشة عمل آر إكس:الملاحظات مقابل الأحداث فيديو وأكمل التحدي المرفق

مرخصة بموجب: CC-BY-SA مع الإسناد
لا تنتمي إلى StackOverflow
scroll top