سؤال

في حين أن مواصفات C# تتضمن معالجًا مسبقًا وتوجيهات أساسية (#define، #if، وما إلى ذلك)، فإن اللغة لا تحتوي على نفس المعالج المسبق المرن الموجود في لغات مثل C/C++.أعتقد أن عدم وجود مثل هذا المعالج المسبق المرن كان قرارًا تصميميًا اتخذه Anders Hejlsberg (على الرغم من أنني، لسوء الحظ، لا أستطيع العثور على إشارة إلى هذا الآن).من التجربة، يعد هذا بالتأكيد قرارًا جيدًا، حيث كانت هناك بعض وحدات الماكرو الرهيبة التي لا يمكن صيانتها والتي تم إنشاؤها عندما كنت أفعل الكثير من C/C++.

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

public string MyProperty
{
  get { return _myProperty; }
  set
  {
    if (value != _myProperty)
    {
      _myProperty = value;
      NotifyPropertyChanged("MyProperty");
      // This line above could be improved by replacing the literal string with
      // a pre-processor directive like "#Property", which could be translated
      // to the string value "MyProperty" This new notify call would be as follows:
      // NotifyPropertyChanged(#Property);
    }
  }
}

هل ستكون فكرة جيدة كتابة معالج مسبق للتعامل مع حالات بسيطة للغاية مثل هذه؟كتب ستيف ماكونيل في اكتمال الكود (ص208):

اكتب المعالج المسبق الخاص بك إذا كانت اللغة لا تتضمن معالجًا مسبقًا، فمن السهل جدًا كتابة واحد...

أنا ممزقة.لقد كان قرار التصميم هو ترك مثل هذا المعالج المسبق المرن خارج لغة C#.ومع ذلك، فإن المؤلف الذي أحترمه بشدة يذكر أنه قد يكون جيدًا في بعض الظروف.

هل يجب علي إنشاء معالج مسبق لـ C#؟هل هناك شخص متاح يقوم بالأشياء البسيطة التي أريد القيام بها؟

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

المحلول

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

نصائح أخرى

هل يجب علي إنشاء معالج مسبق لـ C#؟هل هناك شخص متاح يقوم بالأشياء البسيطة التي أريد القيام بها؟

يمكنك دائمًا استخدام المعالج المسبق للغة C - لغة C# قريبة بدرجة كافية من ناحية بناء الجملة.M4 هو أيضًا خيار.

أعلم أن الكثير من الأشخاص يعتقدون أن الكود القصير يساوي كودًا أنيقًا ولكن هذا ليس صحيحًا.

تم حل المثال الذي تقترحه بشكل مثالي في التعليمات البرمجية، كما أوضحت، ما الذي تحتاجه لتوجيهات المعالج المسبق؟أنت لا ترغب في "المعالجة المسبقة" للتعليمات البرمجية الخاصة بك، بل تريد من المترجم أن يقوم بإدراج بعض التعليمات البرمجية لك في خصائصك.إنه رمز شائع ولكن هذا ليس الغرض من المعالج المسبق.

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

باستخدام معالج مسبق بنمط C++، يمكن اختصار كود OP إلى هذا السطر:

 OBSERVABLE_PROPERTY(string, MyProperty)

ستبدو OBSERVABLE_PROPERTY بهذا الشكل تقريبًا:

#define OBSERVABLE_PROPERTY(propType, propName) \
private propType _##propName; \
public propType propName \
{ \
  get { return _##propName; } \
  set \
  { \
    if (value != _##propName) \
    { \
      _##propName = value; \
      NotifyPropertyChanged(#propName); \
    } \
  } \
}

إذا كان لديك 100 خاصية للتعامل معها، فهذا يعني 1200 سطر تقريبًا من التعليمات البرمجية مقابل 1200 سطر من التعليمات البرمجية.~100.أيهما أسهل في القراءة والفهم؟أيهما أسهل في الكتابة؟

مع لغة C#، بافتراض أنك قمت بالقص واللصق لإنشاء كل خاصية، فإن ذلك يعني 8 عمليات لصق لكل خاصية، بإجمالي 800.مع الماكرو، لا لصق على الإطلاق.ما الذي من المرجح أن يحتوي على أخطاء في الترميز؟أيهما أسهل للتغيير إذا كان عليك الإضافة على سبيل المثال؟علم IsDirty؟

وحدات الماكرو ليست مفيدة عندما يكون هناك احتمال وجود اختلافات مخصصة في عدد كبير من الحالات.

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

الحجة الرئيسية ضد إنشاء معالج مسبق لـ C# هي التكامل في Visual Studio:سيستغرق الأمر الكثير من الجهود (إذا كان ذلك ممكنًا) للحصول على التحسس وتجميع الخلفية الجديدة للعمل بسلاسة.

تتمثل البدائل في استخدام مكون إضافي للإنتاجية Visual Studio مثل ريشاربر أو CodeRush.هذا الأخير لديه - على حد علمي - نظام قوالب لا مثيل له ويأتي مع نظام ممتاز إعادة بناء التعليمات البرمجية أداة.

شيء آخر يمكن أن يكون مفيدًا في حل أنواع المشكلات التي تشير إليها بالضبط هو إطار عمل AOP مثل بوستشارب.
يمكنك بعد ذلك استخدام السمات المخصصة لإضافة وظائف مشتركة.

للحصول على اسم الطريقة المنفذة حاليًا، يمكنك إلقاء نظرة على تتبع المكدس:

public static string GetNameOfCurrentMethod()
{
    // Skip 1 frame (this method call)
    var trace = new System.Diagnostics.StackTrace( 1 );
    var frame = trace.GetFrame( 0 );
    return frame.GetMethod().Name;
}

عندما تكون في طريقة تعيين خاصية، يكون الاسم هو set_Property.

باستخدام نفس التقنية، يمكنك أيضًا الاستعلام عن الملف المصدر ومعلومات السطر/العمود.

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

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

public static string MyPropertyPropertyName
public string MyProperty {
    get { return _myProperty; }
    set {
        if (!String.Equals(value, _myProperty)) {
            _myProperty = value;
            NotifyPropertyChanged(MyPropertyPropertyName);
        }
    }
}

// in the consumer.
private void MyPropertyChangedHandler(object sender,
                                      PropertyChangedEventArgs args) {
    switch (e.PropertyName) {
        case MyClass.MyPropertyPropertyName:
            // Handle property change.
            break;
    }
}

إذا كنت أقوم بتصميم الإصدار التالي من C#، كنت سأفكر في أن تحتوي كل وظيفة على متغير محلي مضمن تلقائيًا يحمل اسم الفئة واسم الوظيفة.في معظم الحالات، يقوم مُحسِّن المترجم بإزالتها.

لست متأكدًا من وجود طلب كبير على هذا النوع من الأشياء بالرغم من ذلك.

@جورج كتب:إذا كنت تريد معالجة التعليمات البرمجية الخاصة بك بطريقة مختلفة، فاستخدم توجيهات المعالج المسبق، ولكن إذا كنت تريد مقتطف التعليمات البرمجية فقط، فابحث عن طريقة أخرى لأنه لم يكن من المفترض أن يقوم المعالج المسبق بذلك.

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

إذا لم يكن هذا هو الاستخدام الصحيح للمعالج المسبق، فماذا يجب أن نسميه استبدال نص بسيط، والذي يجب أن يحدث بشكل عام قبل الترجمة؟

على الأقل بالنسبة للسيناريو المقدم، هناك حل أنظف وآمن للنوع بدلاً من بناء معالج مسبق:

استخدم الأدوية الجنيسة.مثل ذلك:

public static class ObjectExtensions 
{
    public static string PropertyName<TModel, TProperty>( this TModel @this, Expression<Func<TModel, TProperty>> expr )
    {
        Type source = typeof(TModel);
        MemberExpression member = expr.Body as MemberExpression;

        if (member == null)
            throw new ArgumentException(String.Format(
                "Expression '{0}' refers to a method, not a property",
                expr.ToString( )));

        PropertyInfo property = member.Member as PropertyInfo;

        if (property == null)
            throw new ArgumentException(String.Format(
                "Expression '{0}' refers to a field, not a property",
                expr.ToString( )));

        if (source != property.ReflectedType ||
            !source.IsSubclassOf(property.ReflectedType) ||
            !property.ReflectedType.IsAssignableFrom(source))
            throw new ArgumentException(String.Format(
                "Expression '{0}' refers to a property that is not a member of type '{1}'.",
                expr.ToString( ),
                source));

        return property.Name;
    }
}

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

وبما أنه Extension method, ، يمكنك استخدام هذه الطريقة على كل كائن تقريبًا.


كما أن هذا النوع آمن.
لا أستطيع التأكيد على ذلك بما فيه الكفاية.

(أعرف أنه سؤال قديم، ولكنني وجدت أنه يفتقر إلى الحل العملي.)

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

لمزيد من المعلومات حول Boo، راجع هذه الأسئلة ذات الصلة:

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