كيفية تصميم API C# jQuery بحيث لا يكون من المربك استخدامه؟

StackOverflow https://stackoverflow.com/questions/4092776

سؤال

انا صنع استنساخ jQuery ل C#. لقد تم إعداده الآن بحيث تكون كل طريقة طريقة تمديد IEnumerable<HtmlNode> لذلك يعمل بشكل جيد مع المشاريع الحالية التي تستخدم بالفعل HtmlAgilityPack. اعتقدت أنه يمكنني الابتعاد دون الحفاظ على حالة ... ومع ذلك ، لاحظت أن jQuery لديه طريقتان .andSelf و .end الذي "البوب" في أحدث العناصر مطابقة من كومة داخلية. يمكنني محاكاة هذه الوظيفة إذا قمت بتغيير صفي بحيث تعمل دائمًا على كائنات Sharpquery بدلاً من العدوى ، ولكن لا تزال هناك مشكلة.

باستخدام JavaScript ، تم إعطاؤك مستند HTML تلقائيًا ، ولكن عند العمل في C# ، يجب عليك تحميله بشكل صريح ، ويمكنك استخدام أكثر من مستند واحد إذا أردت. يبدو أنه عند الاتصال $('xxx') أنت تنشئ بشكل أساسي كائن jQuery جديد وتبدأ Fresh مع كومة فارغة. في C#، لن ترغب في القيام بذلك ، لأنك لا تريد إعادة تحميل/إعادة صياغة المستند من الويب. بدلاً من ذلك ، يمكنك تحميله مرة واحدة إما إلى كائن Sharpquery ، أو في قائمة htmlnodes (تحتاج فقط إلى DocumentNode للبدء).

في مستندات jQuery ، يقدمون هذا المثال

$('ul.first').find('.foo')
  .css('background-color', 'red')
.end().find('.bar')
  .css('background-color', 'green')
.end();

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

إذن ... كيف يمكنني تصميم واجهة برمجة التطبيقات الخاصة بي؟ هل أضيف آخر Init الطريقة التي يجب أن تبدأ بها جميع الاستعلامات بإعادة ضبط المكدس (ولكن بعد ذلك ، كيف أجبرهم على البدء بذلك؟) ، أو إضافة أ Reset() أن عليهم الاتصال في نهاية خطهم؟ هل أفرط في التحميل [] بدلا من ذلك ويخبرهم أن يبدأوا بذلك؟ هل أقول "انسى ذلك ، لا أحد يستخدم تلك الوظائف المحفوظة للدولة على أي حال؟"

في الأساس ، كيف تريد أن يتم كتابة مثال jQuery في C#؟

  1. sq["ul.first"].Find(".foo") ...
    السقوط: الانتهاك [] منشأه.

  2. sq.Init("ul.first").Find(".foo") ...
    السقوط: لا شيء يجبر المبرمج حقًا على البدء بـ init ، ما لم أضيف بعض آلية "تهيئة" غريبة ؛ قد يحاول المستخدم البدء بـ .Find وليس الحصول على النتيجة التي كان يتوقعها. ايضا، Init و Find متطابقة إلى حد كبير على أي حال ، باستثناء السابق إعادة تعيين المكدس أيضًا.

  3. sq.Find("ul.first").Find(".foo") ... .ClearStack()
    السقوط: قد ينسى المبرمج مسح المكدس.

  4. لا يمكن أن تفعل ذلك.
    end() لم تنفذ.

  5. استخدم كائنين مختلفين.
    ربما الاستخدام HtmlDocument كما القاعدة التي يجب أن تبدأ بها جميع الاستعلامات ، ثم تُرجع كل طريقة بعد ذلك SharpQuery كائن يمكن بالسلاسل. بهذه الطريقة HtmlDocument يحافظ دائمًا على الحالة الأولية ، ولكن SharpQuery قد يكون للكائنات حالات مختلفة. هذا يعني للأسف أنني يجب أن أقوم بتنفيذ مجموعة من الأشياء مرتين (مرة واحدة لـ HTMLDOCUMITY ، مرة واحدة لكائن Sharpquery).

  6. new SharpQuery(sq).Find("ul.first").Find(".foo") ...
    يقوم المُنشئ بنسخ إشارة إلى المستند ، ولكنه يعيد تعيين المكدس.

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

المحلول

أعتقد أن الكتلة الرئيسية المتعثرة التي تتجه إليها هنا هي أنك تحاول الابتعاد عن مجرد الحصول على واحدة SharpQuery كائن لكل مستند. هذا ليس كيف يعمل jQuery ؛ بشكل عام ، فإن كائنات jQuery غير قابلة للتغيير. عندما تسمي طريقة تغير مجموعة العناصر (مثل find أو end أو add) ، لا يغير الكائن الموجود ، لكنه يعيد كائنًا جديدًا:

var theBody = $('body');
// $('body')[0] is the <body>
theBody.find('div').text('This is a div');
// $('body')[0] is still the <body>

(انظر توثيق end لمزيد من المعلومات)

يجب أن تعمل Sharpquery بنفس الطريقة. بمجرد إنشاء كائن Sharpquery مع مستند ، يجب أن تُرجع مكالمات Method جديدة SharpQuery الكائنات ، تشير إلى مجموعة مختلفة من عناصر من نفس المستند. على سبيل المثال:

var sq = SharpQuery.Load(new Uri("http://api.jquery.com/category/selectors/"));
var header = sq.Find("h1"); // doesn't change sq
var allTheLinks = sq.Find(".title-link") // all .title-link in the whole document; also doesn't change sq
var someOfTheLinks = header.Find(".title-link"); // just the .title-link in the <h1>; again, doesn't change sq or header

فوائد هذا النهج هي عدة. لان sq, header, allTheLinks, ، وما إلى ذلك ، كلها في نفس الفصل ، فلديك تطبيق واحد فقط لكل طريقة. ومع ذلك ، يشير كل من هذه الكائنات إلى نفس المستند ، لذلك ليس لديك نسخ متعددة من كل عقدة ، وتنعكس التغييرات في العقد في كل SharpQuery كائن على هذا المستند (على سبيل المثال بعد allTheLinks.text("foo"), someOfTheLinks.text() == "foo".).

تنفيذ end كما أن التلاعب القائم على المكدس الأخرى تصبح سهلة أيضًا. حيث أن كل طريقة تنشئ جديدة ، تمت تصفيتها SharpQuery كائن من آخر ، يحتفظ بالإشارة إلى ذلك الكائن الأصل (allTheLinks إلى header, header إلى sq). ثم end بسيط مثل إرجاع جديد SharpQuery تحتوي على نفس العناصر مثل الوالد ، مثل:

public SharpQuery end()
{
    return new SharpQuery(this.parent.GetAllElements());
}

(أو مهما يهز بناء الجملة.)

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

نصائح أخرى

أود أن أميل نحو متغير في الخيار 2. في jQuery $ () هو استدعاء وظيفة. لا يحتوي C# على وظائف عالمية ، فإن مكالمة الوظائف الثابتة هي الأقرب. أود استخدام طريقة تشير إلى أنك إنشاء غلاف مثل ..

SharpQuery.Create("ul.first").Find(".foo")

لن أكون قلقًا بشأن تقصير Sharpquery إلى SQ لأن Intellisense يعني أن المستخدمين لن يضطروا إلى كتابة كل شيء (وإذا كان لديهم Resharper ، فإنهم يحتاجون فقط إلى كتابة SQ على أي حال).

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