لماذا يجب أن يتم إرسال تعبير لامدا عند توفيره كمعلمة مندوب عادي

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

  •  03-07-2019
  •  | 
  •  

سؤال

خذ الطريقة System.Windows.Forms.Control.Invoc (طريقة التفويض)

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

string str = "woop";
Invoke(() => this.Text = str);
// Error: Cannot convert lambda expression to type 'System.Delegate'
// because it is not a delegate type

ومع ذلك فإن هذا يعمل بشكل جيد:

string str = "woop";
Invoke((Action)(() => this.Text = str));

متى تتوقع الطريقة مندوبًا عاديًا؟

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

المحلول

ويمكن أن تكون إما تحويلها تعبير لامدا إلى نوع مندوب أو شجرة التعبير - لكنه لا يملك أن يعرف <م> التي نوع مندوب. مجرد معرفة التوقيع ليست كافية. على سبيل المثال، لنفترض أن لدي:

public delegate void Action1();
public delegate void Action2();

...

Delegate x = () => Console.WriteLine("hi");

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

إذا كنت تريد أن تجعل من السهل للاتصال Control.Invoke مع Action أسهل ما يمكن فعله هو إضافة وسيلة تمديد التحكم:

public static void Invoke(this Control control, Action action)
{
    control.Invoke((Delegate) action);
}

نصائح أخرى

وتعبت من صب lambdas مرارا وتكرارا؟

public sealed class Lambda<T>
{
    public static Func<T, T> Cast = x => x;
}

public class Example
{
    public void Run()
    {
        // Declare
        var c = Lambda<Func<int, string>>.Cast;
        // Use
        var f1 = c(x => x.ToString());
        var f2 = c(x => "Hello!");
        var f3 = c(x => (x + x).ToString());
    }
}

وتسعة أعشار الناس وقت الحصول على ذلك لأنهم يحاولون حشد على موضوع UI. وإليك الطريقة البطيئة:

static void UI(Action action) 
{ 
  System.Windows.Threading.Dispatcher.CurrentDispatcher.BeginInvoke(action); 
}

والآن بعد أن كتبته، كانت المشكلة تذهب بعيدا (QV انور السكيت) ولدينا بناء الجملة هذا مقتضبة جدا:

int foo = 5;
public void SomeMethod()
{
  var bar = "a string";
  UI(() =>
  {
    //lifting is marvellous, anything in scope where the lambda
    //expression is defined is available to the asynch code
    someTextBlock.Text = string.Format("{0} = {1}", foo, bar);        
  });
}

لنقاط المكافأة هنا طرف آخر. أنت لن تفعل ذلك الاشياء UI لكن في الحالات التي تحتاج SomeMethod لمنع حتى اكتمال (على سبيل المثال طلب / استجابة I / O، في انتظار استجابة) استخدام <لأ href = "http://msdn.microsoft. كوم / EN-US / مكتبة / 9f7e54k1.aspx "يختلط =" نوفولو noreferrer "> WaitHandle (QV MSDN WaitAll، WaitAny، WaitOne).

لاحظ أن AutoResetEvent هو مشتق WaitHandle.

public void BlockingMethod()
{
  AutoResetEvent are = new AutoResetEvent(false);
  ThreadPool.QueueUserWorkItem ((state) =>
  {
    //do asynch stuff        
    are.Set();
  });      
  are.WaitOne(); //don't exit till asynch stuff finishes
}

وتلميح النهائي لأن الأمور يمكن الحصول على متشابكة: WaitHandles المماطلة في الموضوع. وهذا هو ما يفترض القيام به. إذا حاولت حشد على موضوع UI <م> أثناء لديك المتوقفة ، التطبيق سوف معطلا. في هذه الحالة (أ) بعض إعادة بيع ديون خطيرة في النظام، و (ب) كما الإختراق المؤقت الذي يمكن أن ننتظر مثل هذا:

  bool wait = true;
  ThreadPool.QueueUserWorkItem ((state) =>
  {
    //do asynch stuff        
    wait = false;
  });
  while (wait) Thread.Sleep(100);

وبيتر Wone. كنت الرجل دا. أخذ مفهوم الخاص بك قليلا كذلك، خطرت لي هاتين الوظيفتين.

private void UIA(Action action) {this.Invoke(action);}
private T UIF<T>(Func<T> func) {return (T)this.Invoke(func);}

وأضع هاتين الوظيفتين في بلدي التطبيق نموذج، ويمكنني إجراء مكالمات من العمال خلفية مثل هذا

int row = 5;
string ip = UIF<string>(() => this.GetIp(row));
bool r = GoPingIt(ip);
UIA(() => this.SetPing(i, r));

وربما قليلا كسول، ولكن لا يجب أن عامل عمله وظائف الإعداد، والتي تأتي في متناول اليدين فائقة في حالات مثل هذه

private void Ping_DoWork(object sender, System.ComponentModel.DoWorkEventArgs e)
{
  int count = this.dg.Rows.Count;
  System.Threading.Tasks.Parallel.For(0, count, i => 
  {
    string ip = UIF<string>(() => this.GetIp(i));
    bool r = GoPingIt(ip);
    UIA(() => this.SetPing(i, r));
  });
  UIA(() => SetAllControlsEnabled(true));
}

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

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

public sealed class Lambda<S>
{
    public static Func<S, T> CreateFunc<T>(Func<S, T> func)
    {
        return func;
    }

    public static Expression<Func<S, T>> CreateExpression<T>(Expression<Func<S, T>> expression)
    {
        return expression;
    }

    public Func<S, T> Func<T>(Func<S, T> func)
    {
        return func;
    }

    public Expression<Func<S, T>> Expression<T>(Expression<Func<S, T>> expression)
    {
        return expression;
    }
}

حيث اكتب المعلمة S هي المعلمة الرسمية (معلمة الإدخال، وهي الحد الأدنى المطلوب لاستنتاج بقية الأنواع).الآن يمكنك تسميتها مثل:

var l = new Lambda<int>();
var d1 = l.Func(x => x.ToString());
var e1 = l.Expression(x => "Hello!");
var d2 = l.Func(x => x + x);

//or if you have only one lambda, consider a static overload
var e2 = Lambda<int>.CreateExpression(x => "Hello!");

يمكن أن يكون لديك حمولات زائدة إضافية Action<S> و Expression<Action<S>> بالمثل في نفس الفصل.ل آخر المدمج في أنواع المفوض والتعبير، سيتعين عليك كتابة فئات منفصلة مثل Lambda, Lambda<S, T>, Lambda<S, T, U> إلخ.

ميزة هذا أرى على النهج الأصلي:

  1. مواصفات نوع أقل (يجب تحديد المعلمة الرسمية فقط).

  2. مما يمنحك حرية استخدامه ضد أي شخص Func<int, T>, ، وليس فقط متى T هو القول، string, ، كما هو موضح في الأمثلة.

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

    var e = Lambda<Expression<Func<int, string>>>.Cast(x => "Hello!");
    
    //or in case 'Cast' is an instance member on non-generic 'Lambda' class:
    var e = lambda.Cast<Expression<Func<int, string>>>(x => "Hello!");
    

    للتعبيرات.

  4. يعد توسيع الفصل لأنواع المفوضين (والتعبيرات) الأخرى أمرًا مرهقًا بالمثل كما هو مذكور أعلاه.

    var e = Lambda<Action<int>>.Cast(x => x.ToString());
    
    //or for Expression<Action<T>> if 'Cast' is an instance member on non-generic 'Lambda' class:
    var e = lambda.Cast<Expression<Action<int>>>(x => x.ToString());
    

في نهجي، يجب عليك الإعلان عن الأنواع مرة واحدة فقط (وهذا أيضًا أقل لـ Funcس).


إحدى الطرق الأخرى لتنفيذ إجابة أندري هي عدم اتباع طريقة عامة بالكامل

public sealed class Lambda<T>
{
    public static Func<Func<T, object>, Func<T, object>> Func = x => x;
    public static Func<Expression<Func<T, object>>, Expression<Func<T, object>>> Expression = x => x;
}

فتتلخص الأمور في:

var l = Lambda<int>.Expression;
var e1 = l(x => x.ToString());
var e2 = l(x => "Hello!");
var e3 = l(x => x + x);

هذا أقل من ذلك في الكتابة، لكنك تفقد نوعًا معينًا من الأمان، و المنظمة البحرية الدولية، وهذا لا يستحق كل هذا العناء.

وبت في وقت متأخر إلى الحزب ولكن يمكنك أيضا يلقي مثل هذا

this.BeginInvoke((Action)delegate {
    // do awesome stuff
});
 this.Dispatcher.Invoke((Action)(() => { textBox1.Text = "Test 123"; }));
مرخصة بموجب: CC-BY-SA مع الإسناد
لا تنتمي إلى StackOverflow
scroll top