क्या एक सी # लैम्ब्डा घोषित करने और तुरंत इसे कॉल करने का एक तरीका है?


29

लैम्बडा फ़ंक्शन घोषित करना और तुरंत कॉल करना संभव है:

Func<int, int> lambda = (input) => { return 1; };
int output = lambda(0);

अगर एक लाइन में ऐसा करना संभव है तो मैं सोच रहा हूँ, जैसे कुछ

int output = (input) => { return 1; }(0);

जो संकलक त्रुटि "विधि नाम अपेक्षित" देता है। कास्टिंग Func<int, int>या तो काम नहीं करता है:

int output = (Func<int, int>)((input) => { return 1; })(0);

एक ही त्रुटि देता है, और नीचे बताए गए कारणों के लिए मैं इनपुट तर्क प्रकार (पहले int) को स्पष्ट रूप से निर्दिष्ट करने से बचना चाहता हूं ।


आप शायद सोच रहे हैं कि मैं ऐसा क्यों करना चाहता हूं, बजाय कोड को सीधे एम्बेड करने के, जैसे int output = 1;। कारण इस प्रकार है: मैंने SOAP webservice के लिए एक संदर्भ उत्पन्न किया है svcutil, जिसके कारण नेस्टेड तत्व बहुत लंबे वर्ग के नाम उत्पन्न करते हैं, जो मैं टाइप करने से बचना चाहता हूं। इसलिए इसके बजाय

var o = await client.GetOrderAsync(request);
return new Order {
    OrderDate = o.OrderDate,
    ...
    Shipments = o.Shipment_Order == null ? new Shipment[0]
        o.Shipment_Order.Select(sh => new Shipment {
            ShipmentID = sh.ShipmentID,
            ...
            Address = CreateAddress(sh.ReceiverAddress_Shipment);
        }).ToArray()
};

और एक अलग CreateAddress(GetOrderResultOrderShipment_OrderShipmentShipment_Address address)विधि (वास्तविक नाम और भी लंबे हैं, और मुझे फ़ॉर्म के बारे में बहुत सीमित नियंत्रण है), मैं लिखना चाहूंगा

var o = await client.GetOrderAsync(request);
return new Order {
    OrderDate = o.OrderDate,
    ...
    Shipments = o.Shipment_Order == null ? new Shipment[0]
        o.Shipment_Order.Select(sh => new Shipment {
            ShipmentID = sh.ShipmentID,
            ...
            Address = sh.ReceiverAddress_Shipment == null ? null : () => {
                var a = sh.ReceiverAddress_Shipment.Address;
                return new Address {
                    Street = a.Street
                    ...
                };
            }()
        }).ToArray()
};

मुझे पता है कि मैं लिख सकता था

Address = sh.ReceiverAddress_Shipment == null ? null : new Address {
    Street = sh.ReceiverAddress_Shipment.Address.Street,
    ...
}

लेकिन यह भी कि ( sh.ReceiverAddress_Shipment.Addressहिस्सा) बहुत दोहराव हो जाता है अगर कई क्षेत्र हैं। एक लंबोदर की घोषणा करना और तुरंत इसे कॉल करना अधिक सुरुचिपूर्ण कम वर्ण लिखने के लिए होगा।


int output = ((Func<int>) (() => { return 1; }))();
दिमित्री Bychenko

क्यों न केवल एक छोटा आवरण लिखें: public T Exec<T>(Func<T> func) => return func();और इसे इस तरह से उपयोग करें: int x = Exec(() => { return 1; });कि मुझे अपने सभी परगनों के साथ कास्टिंग की तुलना में बहुत अच्छे अच्छे पढ़ता है।
जर्मि

@germi अच्छा विचार है, लेकिन यह मुझे देता है "विधि Exec के प्रकार तर्क उपयोग से अनुमान नहीं लगाया जा सकता है।"
ग्लोरफाइंडेल

@Glorfindel आपने कुछ गलत किया, फिर: dotnetfiddle.net/oku7eX
canton7

@ canton7 क्योंकि मैं वास्तव में इनपुट पैरामीटर के साथ एक लैम्ब्डा का उपयोग कर रहा हूं ... धन्यवाद, यह अब काम करता है।
ग्लोरफाइंडेल

जवाबों:


29

मेमने को डालने की कोशिश करने के बजाय, मैं आपको एक छोटे से सहायक कार्य का उपयोग करने का प्रस्ताव देता हूं:

public static TOut Exec<TIn, TOut>(Func<TIn, TOut> func, TIn input) => func(input);

जिसके बाद आप इस तरह का उपयोग कर सकते हैं int x = Exec(myVar => myVar + 2, 0);:। यहाँ सुझाए गए विकल्पों की तुलना में यह मेरे लिए बहुत अच्छा है।


25

यह बदसूरत है, लेकिन यह संभव है:

int output = ((Func<int, int>)(input => { return 1; }))(0);

आप कास्ट कर सकते हैं, लेकिन लैम्ब्डा को कोष्ठकों में संलग्न करने की आवश्यकता है।

उपरोक्त सरलीकृत किया जा सकता है:

int output = ((Func<int, int>)(input => 1))(0);

2
आह, बिल्कुल। मैंने केवल कोशिश की int output = (Func<int>)(() => { return 1; })();लेकिन कलाकारों की लंबोदर फांसी की तुलना में कम प्राथमिकता है।
ग्लोरफाइंडेल

यह अभी भी बहुत लंबे वर्ग के नाम लिखने के लिए नहीं चाहते हैं की समस्या को हल नहीं करता है।
ग्लोरफाइंडेल

4

C # में लैम्ब्डा के शाब्दिक अर्थ में एक जिज्ञासा है कि उनका अर्थ उनके प्रकार पर निर्भर है। वे अनिवार्य रूप से अपने रिटर्न प्रकार पर अतिभारित होते हैं जो कि C # में कहीं और मौजूद नहीं है। (न्यूमेरिक लिटरल कुछ इसी तरह के हैं।)

ठीक उसी लैम्ब्डा शाब्दिक कर सकते हैं या तो एक गुमनाम समारोह है कि आप निष्पादित कर सकते हैं (यानी एक के रूप में मूल्यांकित Func/ Action) या की तरह एक सार सिंटेक्स पेड़ की तरह (यानी एक LINQ अभिव्यक्ति ट्री), शरीर के अंदर आपरेशन के एक सार प्रतिनिधित्व।

उत्तरार्द्ध है, उदाहरण के लिए, LINQ to SQL, LINQ to XML, आदि कैसे काम करते हैं: लैम्ब्डा निष्पादन योग्य कोड का मूल्यांकन नहीं करते हैं , वे LINQ अभिव्यक्ति पेड़ों का मूल्यांकन करते हैं, और LINQ प्रदाता तब उन एक्सप्रेशन ट्री का उपयोग कर समझ सकते हैं कि क्या है लैम्ब्डा का शरीर कर रहा है और उससे उत्पन्न एसक्यूएल क्वेरी की तरह है।

आपके मामले में, संकलक के लिए यह जानने का कोई तरीका नहीं है कि घर में लैम्ब्डा शाब्दिक Funcया एक LINQ अभिव्यक्ति का मूल्यांकन किया जाए । यही कारण है कि जॉनाथन बार्कले का जवाब काम करता है: यह एक प्रकार का मेमने की अभिव्यक्ति देता है और इसलिए, संकलक को पता है कि आप एक Funcसंकलित कोड के साथ चाहते हैं जो बिना मूल्यांकन किए गए LINQ अभिव्यक्ति ट्री के बजाय आपके लैम्ब्डा के शरीर को निष्पादित करता है जो अंदर कोड का प्रतिनिधित्व करता है लंबोदर का शरीर।


3

आप कर की घोषणा को इनलाइन Funcकर सकते हैं

int output = (new Func<int, int>(() => { return 1; }))(0);

और तुरंत इसे लागू करना।


2

आप Selectविधि में उपनाम भी बना सकते हैं

var o = await client.GetOrderAsync(request);
return new Order {
    OrderDate = o.OrderDate,
    ...
    Shipments = o.Shipment_Order == null ? new Shipment[0]
        o.Shipment_Order.Select(sh => {
          var s = sh.ReceiverAddress_Shipment;
          var a = s.Address;
          return new Shipment {
            ShipmentID = sh.ShipmentID,
            ...
            Address = s == null ? 
                      null : 
                      new Address {
                        Street = a.Street
                        ...
                      }
          };
        }).ToArray()
};

या ??ऑपरेटर के साथ

var o = await client.GetOrderAsync(request);
return new Order {
    OrderDate = o.OrderDate,
    ...
    Shipments = o.Shipment_Order?.Select(sh => {
        var s = sh.ReceiverAddress_Shipment;
        var a = s.Address;
        return new Shipment {
            ShipmentID = sh.ShipmentID,
            ...
            Address = s == null ? 
                      null : 
                      new Address {
                          Street = a.Street
                          ...
                      }
        };
    }).ToArray() ?? new Shipment[0]
};

1

यदि आप कुछ विस्तार विधियों के डिजाइन दिशानिर्देशों का उल्लंघन करने से गुरेज नहीं करते हैं, तो नल-सशर्त ऑपरेटर के साथ संयुक्त विस्तार विधियां ?.आपको यथोचित रूप से ले जा सकती हैं:

public static class Extensions
{
    public static TOut Map<TIn, TOut>(this TIn value, Func<TIn, TOut> map)
        where TIn : class
        => value == null ? default(TOut) : map(value);

    public static IEnumerable<T> OrEmpty<T>(this IEnumerable<T> items)
        => items ?? Enumerable.Empty<T>();
}

यह आपको देगा:

return new Order
{
    OrderDate = o.OrderDate,
    Shipments = o.Shipment_Order.OrEmpty().Select(sh => new Shipment
    {
        ShipmentID = sh.ShipmentID,
        Address = sh.ReceiverAddress_Shipment?.Address.Map(a => new Address
        {
            Street = a.Street
        })
    }).ToArray()
};

और यदि आपको अधिकतर सरणियों की आवश्यकता है, तो ToArrayकुछ और विधि कॉलों को एनकैप्सुलेट करने के लिए एक्सटेंशन विधि को ओवरराइड करें:

public static TOut[] ToArray<TIn, TOut>(this IEnumerable<TIn> items, Func<TIn, TOut> map)
    => items == null ? new TOut[0] : items.Select(map).ToArray();

जिसके परिणामस्वरूप:

return new Order
{
    OrderDate = o.OrderDate,
    Shipments = o.Shipment_Order.ToArray(sh => new Shipment
    {
        ShipmentID = sh.ShipmentID,
        Address = sh.ReceiverAddress_Shipment?.Address.Map(a => new Address
        {
            Street = a.Street
        })
    })
};
हमारी साइट का प्रयोग करके, आप स्वीकार करते हैं कि आपने हमारी Cookie Policy और निजता नीति को पढ़ और समझा लिया है।
Licensed under cc by-sa 3.0 with attribution required.