যখন সরল প্রতিনিধি পরামিতি হিসাবে সরবরাহ করা হয় তখন ল্যাম্বডা এক্সপ্রেশন কেন অবশ্যই কাটতে হবে


124

পদ্ধতি.সামগ্রী। উইন্ডোজ.ফর্মস.কন্ট্রোল.ইনভোক (ডেলিগেট পদ্ধতি) নিন

কেন এটি একটি সংকলন সময় ত্রুটি দেয়:

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));

পদ্ধতিটি যখন কোনও সরল প্রতিনিধি প্রত্যাশা করে?

উত্তর:


125

ল্যাম্বডা এক্সপ্রেশনটি হয় ডেলিগেট টাইপ বা এক্সপ্রেশন ট্রিতে রূপান্তরিত হতে পারে - তবে এটি জানতে হবে কোন প্রতিনিধি প্রকারটি। শুধু স্বাক্ষর জানার পক্ষে যথেষ্ট নয়। উদাহরণস্বরূপ, ধরুন আমার কাছে রয়েছে:

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);
}

1
ধন্যবাদ - আমি প্রশ্নটি আপডেট করেছি কারণ আমি মনে করি যে টাইপড ব্যবহার করার জন্য ভুল শব্দ ছিল।
xyz

1
এটি একটি খুব মার্জিত এবং পরিপক্ক সমাধান। আমি সম্ভবত এটি "ইনভোক অ্যাকশন" বলব যাতে নামটি প্রস্তাব দেয় যে আমরা আসলে যাচ্ছি (জেনেরিক প্রতিনিধি পরিবর্তে) তবে এটি আমার পক্ষে অবশ্যই কার্যকর হয় :)
ম্যাথিয়াস হরিণিসাক

7
আমি এটি "খুব কম দরকারী এবং ..." এর সাথে একমত নই। কোনও ল্যাম্বডা দিয়ে বিগিন / আহ্বান করার ক্ষেত্রে আপনি ডেলিগেটের ধরণটি স্বয়ংক্রিয়ভাবে উত্পাদিত হয়েছে কিনা তা অবশ্যই নিশ্চিত করবেন না, আমরা কেবল কলটি পেতে চাই। কোন পরিস্থিতিতে ডেলিগেট গ্রহণের (বেস টাইপ) কোনও পদ্ধতি কংক্রিটের ধরণ কী তা যত্নশীল হবে? এছাড়াও, এক্সটেনশন পদ্ধতির উদ্দেশ্য কী? এটি কিছু সহজ করে না।
তারিভিভার

5
আহ! আমি এক্সটেনশন পদ্ধতি যুক্ত করেছি এবং চেষ্টা করেছি Invoke(()=>DoStuff)এবং তবুও ত্রুটি পেয়েছি। সমস্যাটি হ'ল আমি অন্তর্নিহিত 'এটি' ব্যবহার করেছি। এটি একটি কন্ট্রোল সদস্য আপনার স্পষ্ট হতে হবে মধ্যে থেকে কাজ পেতে: this.Invoke(()=>DoStuff)
তার্জিভার

2
অন্য কারও এটি পড়ার জন্য, আমি মনে করি সি # এর প্রশ্নের উত্তর এবং উত্তরগুলি : ইনভোকেরওয়েড কোড কোডটি স্বয়ংক্রিয় করা খুব সহায়ক।
এরিক ফিলিপস

34

বারবার লম্বডাস castালাই করে ক্লান্ত?

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());
    }
}

3
এটি জেনেরিকের একটি সুন্দর ব্যবহার।
পিটার Wone

2
আমাকে স্বীকার করতে হবে, এটি কেন কাজ করেছে তা নির্ধারণ করতে আমাকে কিছুটা সময় নিয়েছিল। উজ্জ্বল। খুব খারাপ আমার এখনই এটির কোনও ব্যবহার নেই।
উইলিয়াম

1
আপনি কি এর ব্যবহার ব্যাখ্যা করতে পারেন? আমার পক্ষে এটি বোঝা কি মুশকিল? অনেক ধন্যবাদ.
শাহকল্পেশ

এটি পড়ার জন্য এটি এটিকে একা বলা যাক তবে আমি মনে করি যে আমি জোন স্কিটির উত্তরটি পছন্দ করি!
পোগ্রিন্ডিস

@ শাহকল্পেশ এটি খুব জটিল নয়। এটিকে দেখুন, Lambda<T>ক্লাসটির একটি পরিচয় রূপান্তর পদ্ধতি বলা আছে Cast, যা পাস ( Func<T, T>) দিয়ে যা দেয় তা ফেরত দেয় । এখন Lambda<T>ঘোষিত হয় যেমন Lambda<Func<int, string>>যার অর্থ আপনি একটি পাস Func<int, string>থেকে Castপদ্ধতি, এটা ফেরৎ Func<int, string>পিছনে, যেহেতু Tএই ক্ষেত্রে দেখা যায় Func<int, string>
নওফল

12

লোকেরা এটি পেয়ে যায় তার দশ ভাগ দশমাংশ কারণ তারা ইউআই থ্রেডে মার্শাল করার চেষ্টা করছে। অলস উপায় এখানে:

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

এখন এটি টাইপ করার পরে, সমস্যাটি চলে যায় (কিউভি স্কিটির অ্যাওয়ার) এবং আমাদের কাছে এটি অত্যন্ত সংশ্লেষের বাক্য গঠন রয়েছে:

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);        
  });
}

বোনাস পয়েন্টের জন্য এখানে অন্য টিপস। আপনি এটি ইউআই স্টাফের জন্য করবেন না তবে এমন ক্ষেত্রে যেখানে আপনার সামমথোডের এটি শেষ না হওয়া অবধি ব্লক করা দরকার (যেমন অনুরোধ / প্রতিক্রিয়া I / O, প্রতিক্রিয়াটির জন্য অপেক্ষা করছেন) একটি ওয়েটহ্যান্ডল (qv msdn WaitAll, WaitAny, WaitOne) ব্যবহার করুন।

নোট করুন যে অটোসেটসেন্ট একটি ওয়েটহ্যান্ডল ডেরিভেটিভ।

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
}

এবং একটি চূড়ান্ত টিপ কারণ জিনিসগুলি জঞ্জাল হতে পারে: ওয়েটহ্যান্ডলস থ্রেডটি স্টল করুন। তাদের এই কাজটি করার কথা। আপনি যদি থেমে থাকা UI থ্রেডে মার্শাল করার চেষ্টা করেন তবে আপনার অ্যাপটি স্তব্ধ হয়ে যাবে। এই ক্ষেত্রে (ক) কিছু গুরুতর রিফ্যাক্টরিং ক্রমযুক্ত, এবং (খ) অস্থায়ী হ্যাক হিসাবে আপনি এটির মতো অপেক্ষা করতে পারেন:

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

3
আমি এটি আকর্ষণীয় মনে করি যে লোকেরা কেবল উত্তর দেওয়ার জন্য গাল পেয়েছে কারণ তারা ব্যক্তিগতভাবে এটি আকর্ষণীয় মনে করে না। যদি এটি ভুল হয় এবং আপনি এটি জানেন, তবে এতে কী দোষ রয়েছে তা বলুন। যদি আপনি এটি করতে না পারেন তবে আপনার ডাউন ডাউনের কোনও ভিত্তি নেই। এটি যদি সত্যই ভুল হয় তবে "বালুনি। দেখুন [সঠিক প্রতিক্রিয়া]" বা সম্ভবত "প্রস্তাবিত সমাধান নয়, দেখুন [আরও ভাল জিনিস]"
পিটার ওয়োন

1
হ্যাঁ, আমি প্রকৃতপক্ষে; তবে যাইহোক আমার কেন ধারণা ছিল না যে এটি কেন ভোটে নিযুক্ত হয়েছিল; যদিও আমি প্রকৃত কোডটি ব্যবহার করি নি, তবুও আমি ভেবেছিলাম এটি ইউআই ক্রস-থ্রেডের জন্য একটি দুর্দান্ত দ্রুত পরিচয়। এবং এটিতে এমন কিছু জিনিস রয়েছে যা আমি এতগুলি কুদোস সম্পর্কে সত্যই ভাবি নি, অবশ্যই উপরে এবং বাইরে যাওয়ার জন্য +1। :) আমি বলতে চাইছি, আপনি ডেলিগেটের দাওয়াত দেওয়ার জন্য একটি দুর্দান্ত দ্রুত পদ্ধতি দিয়েছেন; আপনি কলগুলির জন্য একটি বিকল্প দেন যা অবশ্যই অপেক্ষা করা উচিত; এবং আপনি যে কেউ ইউআই থ্রেড হেল্কে আটকে আছেন তার কিছুটা নিয়ন্ত্রণ ফিরে পাওয়ার জন্য দুর্দান্ত দ্রুততার সাথে এটি অনুসরণ করুন। উত্তম উত্তর, আমিও + <3 বলতে চাই। :)
শেলিবিটারফ্লাই

System.Windows.Threading.Dispatcher.CurrentDispatcherCURRENT থ্রেডের প্রেরককে ফিরিয়ে দেবে - যেমন আপনি যদি কোনও থ্রেড থেকে এই পদ্ধতিটি কল করেন যা UI থ্রেড নয়, কোডটি ইউআই থ্রেডে চলবে না।
BrainSlugs83

@ ব্রেইনস্লাগস ৩ good ভাল বিষয়, সম্ভবত কোনও অ্যাপ্লিকেশনটির জন্য ইউআই থ্রেড প্রেরণের কোনও রেফারেন্স ক্যাপচার করা এবং এটি বিশ্বব্যাপী অ্যাক্সেসযোগ্য কোনও জায়গায় রেখে দেওয়া ভাল। আমি অবাক হয়েছি যে কেউ এটি খেয়াল করতে দীর্ঘ সময় নিয়েছে!
পিটার উইন

4

পিটার ওয়োন তুমি দা মানুষ আপনার ধারণাটি আরও খানিকটা এগিয়ে নিয়ে আমি এই দুটি কার্য নিয়ে এসেছি।

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));
}

মূলত, গুই ডেটাগ্রিডভিউ থেকে কিছু আইপি ঠিকানাগুলি পান, তাদের পিং করুন, ফলস আইকনগুলিকে সবুজ বা লালতে সেট করুন এবং ফর্মটিতে পুনরায় চালুযোগ্য বোতামগুলি। হ্যাঁ, এটি একটি ব্যাকগ্রাউন্ড ওয়ার্কারে "সমান্তরাল। জন্য"। হ্যাঁ এটি প্রচুর ওভারহেডকে ডাকে, তবে এটি সংক্ষিপ্ত তালিকা এবং আরও অনেক কমপ্যাক্ট কোডের জন্য উপেক্ষিত।


1

আমি @ অ্যান্ড্রে নুমভের জবাবের ভিত্তিতে এটি তৈরির চেষ্টা করেছি । এটি একটি সামান্য উন্নতি হতে পারে।

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!");

আপনি অতিরিক্ত overloads থাকতে পারে 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);

এটি এমনকি টাইপিংও কম, তবে আপনি নির্দিষ্ট ধরণের সুরক্ষা এবং ইমো হারাবেন এটি উপযুক্ত নয়।


1

পার্টিতে দেরি করে ফেলুন তবে আপনি এটিও পছন্দ করতে পারেন

this.BeginInvoke((Action)delegate {
    // do awesome stuff
});


0

এক্সউনিট এবং ফ্লুয়েন্ট অ্যাসেসরেন্সগুলির সাথে খেলে এই ইনলাইন ক্ষমতাটি এমনভাবে ব্যবহার করা সম্ভব হয়েছিল যাতে আমি সত্যিই দুর্দান্ত।

আগে

[Fact]
public void Pass_Open_Connection_Without_Provider()
{
    Action action = () => {
        using (var c = DbProviderFactories.GetFactory("MySql.Data.MySqlClient").CreateConnection())
        {
            c.ConnectionString = "<xxx>";
            c.Open();
        }
    };

    action.Should().Throw<Exception>().WithMessage("xxx");
}

পরে

[Fact]
public void Pass_Open_Connection_Without_Provider()
{
    ((Action)(() => {
        using (var c = DbProviderFactories.GetFactory("<provider>").CreateConnection())
        {
            c.ConnectionString = "<connection>";
            c.Open();
        }
    })).Should().Throw<Exception>().WithMessage("Unable to find the requested .Net Framework Data Provider.  It may not be installed.");
}
আমাদের সাইট ব্যবহার করে, আপনি স্বীকার করেছেন যে আপনি আমাদের কুকি নীতি এবং গোপনীয়তা নীতিটি পড়েছেন এবং বুঝতে পেরেছেন ।
Licensed under cc by-sa 3.0 with attribution required.