Bir C # lambda bildirmek ve hemen çağırmak için bir yolu var mı?


29

Bir lambda işlevi bildirmek ve hemen çağırmak mümkündür:

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

Bir satırda bunu yapmanın mümkün olup olmadığını merak ediyorum, örneğin

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

derleyici hatası "Yöntem adı bekleniyor" verir. İçin yayınlama da Func<int, int>çalışmıyor:

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

aynı hatayı verir ve aşağıda belirtilen nedenlerden ötürü, giriş bağımsız değişken türünü (ilk int) açıkça belirtmek istemiyorum .


Muhtemelen sadece kodu doğrudan gömmek yerine neden bunu yapmak istediğimi merak ediyorsunuz , örneğin int output = 1;. Nedeni aşağıdaki gibidir: svcutilİç içe geçmiş öğeler nedeniyle yazmak zorunda kalmamak için son derece uzun sınıf adları üreten bir SOAP web hizmeti için bir başvuru oluşturdum. Yani yerine

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

ve ayrı bir CreateAddress(GetOrderResultOrderShipment_OrderShipmentShipment_Address address)yöntem (gerçek isimler daha da uzun ve form üzerinde çok sınırlı kontrole sahibim) yazmak istiyorum

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

Yazabileceğimi biliyorum

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

ancak bu ( sh.ReceiverAddress_Shipment.Addressbölüm) bile çok alan varsa çok tekrarlanır hale gelir. Bir lambda ilan etmek ve hemen çağırmak, daha az karakter yazmak için daha zarif olurdu .


int output = ((Func<int>) (() => { return 1; }))();
Dmitry Bychenko

Neden sadece küçük bir sargı yazmıyorum: public T Exec<T>(Func<T> func) => return func();ve bunu şöyle kullanın: int x = Exec(() => { return 1; });Bana göre tüm parensleri ile dökümden çok daha güzel okuyor.
germi

@germi güzel bir fikir, ama bana "Exec yöntemi için tür bağımsız değişkenleri kullanımdan çıkarılamaz."
Glorfindel

@Glorfindel O zaman yanlış bir şey yaptınız: dotnetfiddle.net/oku7eX
canton7

@ canton7 çünkü aslında giriş parametresi olan bir lambda kullanıyorum ... Teşekkürler, şimdi çalışıyor.
Glorfindel

Yanıtlar:


29

Lambda'yı atmaya çalışmak yerine, küçük bir yardımcı işlev kullanmanızı öneriyorum:

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

hangi sonra böyle kullanabilirsiniz: int x = Exec(myVar => myVar + 2, 0);. Bu benim için burada önerilen alternatiflerden çok daha güzel okuyor.


25

Bu çirkin ama mümkün:

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

Dökebilirsiniz, ancak lambda'nın parantez içine alınması gerekir.

Yukarıdakiler de basitleştirilebilir:

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

2
Ah, elbette. Sadece denedim int output = (Func<int>)(() => { return 1; })();ama oyuncu kadrosu lambda uygulamasından daha düşük önceliğe sahip.
Glorfindel

Yine de, son derece uzun sınıf isimlerini yazmak istememe sorununu hala çözmüyor.
Glorfindel

4

C # 'daki Lambda değişmezleri, anlamlarının türlerine bağlı olması bakımından ilginç bir ayrım yapar. Aslında C # 'da başka bir yerde olmayan bir şey olan dönüş türlerine aşırı yüklenmiştir . (Sayısal değişmez değerler biraz benzer.)

Aynı lambda değişmezi olabilir ya sen yürütmek ki isimsiz bir işlev (yani için değerlendirmek Func/ Action) veya tür (yani bir LINQ İfade Ağacı) bir soyut sözdizimi Ağacı benzeri, Gövde iç operasyonların soyut temsili.

İkincisi deyişle, örneğin, nasıl SQL, XML LINQ vb LINQ çalışma: lambda'lar yok çalıştırılabilir kod değerlendirmek, onlar LINQ ifadesi Ağaçlar değerlendirmek ve LINQ sağlayıcısı sonra ne anlamak olanlar İfade Ağaçlar kullanabilirsiniz lambda gövdesi yapıyor ve örneğin bir SQL Sorgusu üretiyor.

Sizin durumunuzda, derleyicinin lambda değişmezinin bir Funcveya bir LINQ İfadesi ile ne kadar değerlendirileceğini bilmesi mümkün değildir . Bu yüzden Johnathan Barclay'ın cevabı işe yarıyor: lambda ifadesine bir tür veriyor ve bu nedenle derleyici , içindeki kodu temsil eden değerlendirilmemiş bir LINQ İfade Ağacı yerine lambda'nızın gövdesini çalıştıranFunc derlenmiş bir kodla istediğinizi biliyor lambda gövdesi.


3

FuncYaparak inline

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

ve hemen çağırıyor.


2

SelectYöntemde takma ad da oluşturabilirsiniz

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

veya ??operatörle

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

Uzantı yöntemleri tasarım yönergelerinden birkaçını ihlal etmenin bir sakıncası yoksa , boş koşullu operatörle birlikte uzantı yöntemleri ?.sizi makul ölçüde uzağa götürebilir:

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

size bunu verecektir:

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

ve çoğunlukla dizilere ihtiyacınız varsa, ToArraybirkaç yöntem çağrısını kapsüllemek için uzantı yöntemini geçersiz kılın :

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

sonuçlanan:

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
        })
    })
};
Sitemizi kullandığınızda şunları okuyup anladığınızı kabul etmiş olursunuz: Çerez Politikası ve Gizlilik Politikası.
Licensed under cc by-sa 3.0 with attribution required.