سؤال

لدي الكود التالي:

Func<string, bool> comparer = delegate(string value) {
    return value != "0";
};

ومع ذلك، لا يتم تجميع ما يلي:

var comparer = delegate(string value) {
    return value != "0";
};

لماذا لا يستطيع المترجم معرفة أنه ملف Func<string, bool>؟يأخذ معلمة سلسلة واحدة، ويعيد قيمة منطقية.بدلا من ذلك، فإنه يعطيني الخطأ:

لا يمكن تعيين طريقة مجهولة للمتغير المحلي المغطى ضمنيًا.

لدي تخمين واحد وهذا هو إذا تم تجميع الإصدار var, ، سيفتقر إلى الاتساق إذا كان لدي ما يلي:

var comparer = delegate(string arg1, string arg2, string arg3, string arg4, string arg5) {
    return false;
};

ما ورد أعلاه لن يكون منطقيًا نظرًا لأن Func<> يسمح فقط بما يصل إلى 4 وسائط (في .NET 3.5، وهو ما أستخدمه).ربما يمكن لشخص ما توضيح المشكلة.شكرًا.

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

المحلول

لقد أشار آخرون بالفعل إلى أن هناك عددًا لا حصر له من أنواع المفوضين المحتملين الذين يمكنك اختيارهم استطاع قصدت؛ما الذي يميز هذا الأمر؟ Func أنه يستحق أن يكون الافتراضي بدلا من Predicate أو Action أو أي احتمال آخر؟وبالنسبة للامدا، لماذا من الواضح أن النية هي اختيار صيغة المندوب، بدلاً من صيغة شجرة التعبير؟

ولكن يمكننا أن نقول ذلك Func خاص، وأن النوع المستنتج من طريقة لامدا أو الطريقة المجهولة هو وظيفة شيء ما.لا يزال لدينا جميع أنواع المشاكل.ما هي الأنواع التي ترغب في استنتاجها للحالات التالية؟

var x1 = (ref int y)=>123;

لا يوجد Func<T> النوع الذي يأخذ المرجع أي شيء.

var x2 = y=>123;

نحن لا نعرف نوع المعلمة الرسمية، على الرغم من أننا نعرف الإرجاع.(أم نحن؟هل العودة صحيحة؟طويل؟قصير؟بايت؟)

var x3 = (int y)=>null;

لا نعرف نوع الإرجاع، لكن لا يمكن أن يكون باطلا.يمكن أن يكون نوع الإرجاع أي نوع مرجعي أو أي نوع قيمة فارغة.

var x4 = (int y)=>{ throw new Exception(); }

مرة أخرى، نحن لا نعرف نوع الإرجاع، وهذه المرة يستطيع يكون باطلا.

var x5 = (int y)=> q += y;

هل المقصود من ذلك أن يكون عبارة لامدا ذات إرجاع باطلة أو شيئًا يُرجع القيمة التي تم تعيينها لـ q؟كلاهما قانوني.الذي يجب أن نختار؟

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

الموقف الذي يكون فيه مفيدًا بالفعل هو:

var xAnon = (int y)=>new { Y = y };

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

Func<A, R> WorkItOut<A, R>(Func<A, R> f) { return f; }
...
var xAnon = WorkItOut((int y)=>new { Y = y });

والآن يعمل استنتاج نوع الطريقة على تحديد نوع func.

نصائح أخرى

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

النظر في المثال الخاص بك:

var comparer = delegate(string value) { return value != "0"; };

فيما يلي استنتاجان محتملان لما var يجب ان يكون:

Predicate<string> comparer  = delegate(string value) { return value != "0"; };  // okay
Func<string, bool> comparer = delegate(string value) { return value != "0"; };  // also okay

أي واحد يجب أن يستنتج المترجم؟ليس هناك سبب وجيه لاختيار واحد أو آخر.وعلى الرغم من أ Predicate<T> يعادل وظيفيا ل Func<T, bool>, ، فهي لا تزال أنواعًا مختلفة على مستوى نظام النوع .NET.لذلك لا يمكن للمترجم حل نوع المفوض بشكل لا لبس فيه، ويجب أن يفشل في استنتاج النوع.

إريك ليبرت لديه قديم بريد عنه حيث يقول

وفي الواقع ، تستدعي مواصفات C# 2.0 هذا.تعبيرات مجموعة الأسلوب والتعبيرات المجهول هي تعبيرات لا تحتوي على تعبيرات في C# 2.0 ، وينضم إليهم تعبيرات Lambda في C# 3.0.لذلك من غير القانوني بالنسبة لهم أن يظهروا "عارياً" على الجانب الأيمن من إعلان ضمني.

يعتبر المندوبون المختلفون أنواعًا مختلفة.على سبيل المثال، Action يختلف عن MethodInvoker, ، ومثال على ذلك Action لا يمكن تعيينه لمتغير من النوع MethodInvoker.

لذلك، نظرا لمندوب مجهول (أو لامدا) مثل () => {}, ، هل هو Action أو أ MethodInvoker؟المترجم لا يستطيع أن يقول.

وبالمثل، إذا أعلنت أن نوع المفوض يأخذ a string الحجة والعودة أ bool, ، كيف سيعرف المترجم أنك تريد حقًا ملف Func<string, bool> بدلا من نوع المفوض الخاص بي؟لا يمكن استنتاج نوع المفوض.

النقاط التالية مأخوذة من MSDN فيما يتعلق بالمتغيرات المحلية المكتوبة ضمنيًا:

  1. لا يمكن استخدام var إلا عند الإعلان عن متغير محلي وتهيئته في نفس العبارة؛لا يمكن تهيئة المتغير إلى قيمة خالية أو إلى مجموعة أسلوب أو وظيفة مجهولة.
  2. ترشد الكلمة الأساسية var المترجم إلى استنتاج نوع المتغير من التعبير الموجود على الجانب الأيمن من عبارة التهيئة.
  3. من المهم أن نفهم أن الكلمة الأساسية var لا تعني "متغير" ولا تشير إلى أن المتغير مكتوب بشكل فضفاض أو مرتبط متأخرًا.هذا يعني فقط أن المترجم يحدد ويعين النوع الأنسب.

مرجع MSDN:المتغيرات المحلية المكتوبة ضمنيًا

مع الأخذ في الاعتبار ما يلي فيما يتعلق بالطرق المجهولة:

  1. تمكنك الأساليب المجهولة من حذف قائمة المعلمات.

مرجع MSDN:طرق مجهولة

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

مشاركتي لا تجيب على السؤال الفعلي، ولكنها تجيب على السؤال الأساسي:

"كيف أتجنب الاضطرار إلى كتابة نوع قبيح مثل Func<string, string, int, CustomInputType, bool, ReturnType>?" [1]

لكوني مبرمجًا كسولًا/مخترقًا، فقد جربت استخدامه Func<dynamic, object> - الذي يأخذ معلمة إدخال واحدة ويعيد كائنًا.

بالنسبة للوسيطات المتعددة، يمكنك استخدامها كما يلي:

dynamic myParams = new ExpandoObject();
myParams.arg0 = "whatever";
myParams.arg1 = 3;
Func<dynamic, object> y = (dynObj) =>
{
    return dynObj.arg0.ToUpper() + (dynObj.arg1 * 45); //screw type casting, amirite?
};
Console.WriteLine(y(myParams));

نصيحة:يمكنك استخدام Action<dynamic> إذا لم تكن بحاجة إلى إرجاع كائن.

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

أنا مبتدئ جدًا في المندوبين.أردت فقط أن أشارك ما تعلمته.


[1] يفترض هذا أنك لا تتصل بطريقة تتطلب إجراءً محددًا مسبقًا Func كمعلمة، وفي هذه الحالة، سيتعين عليك كتابة تلك السلسلة الفظيعة :/

ماذا عن ذلك؟

var item = new
    {
        toolisn = 100,
        LangId = "ENG",
        toolPath = (Func<int, string, string>) delegate(int toolisn, string LangId)
        {
              var path = "/Content/Tool_" + toolisn + "_" + LangId + "/story.html";
              return File.Exists(Server.MapPath(path)) ? "<a style=\"vertical-align:super\" href=\"" + path + "\" target=\"_blank\">execute example</a> " : "";
        }
};

string result = item.toolPath(item.toolisn, item.LangId);
مرخصة بموجب: CC-BY-SA مع الإسناد
لا تنتمي إلى StackOverflow
scroll top