Có cách nào để khai báo C # lambda và gọi ngay cho nó không?


29

Có thể khai báo hàm lambda và gọi ngay cho nó:

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

Tôi tự hỏi nếu nó có thể làm như vậy trong một dòng, ví dụ như một cái gì đó như

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

cung cấp một lỗi biên dịch "Tên phương thức dự kiến". Đúc để Func<int, int>không hoạt động:

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

đưa ra cùng một lỗi và vì những lý do được đề cập dưới đây, tôi muốn tránh phải chỉ định rõ ràng loại đối số đầu vào (đầu tiên int).


Có lẽ bạn đang tự hỏi tại sao tôi muốn làm điều này, thay vì chỉ nhúng mã trực tiếp, ví dụ int output = 1;. Lý do là như sau: Tôi đã tạo một tham chiếu cho dịch vụ web SOAP với svcutil, vì các phần tử lồng nhau tạo ra các tên lớp cực kỳ dài, mà tôi muốn tránh phải gõ ra. Vì vậy, thay vì

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

và một CreateAddress(GetOrderResultOrderShipment_OrderShipmentShipment_Address address)phương thức riêng biệt (tên thật thậm chí còn dài hơn và tôi có quyền kiểm soát rất hạn chế về biểu mẫu), tôi muốn viết

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

Tôi biết tôi có thể viết

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

nhưng ngay cả điều đó ( sh.ReceiverAddress_Shipment.Addressphần) trở nên rất lặp đi lặp lại nếu có nhiều lĩnh vực. Tuyên bố một lambda và ngay lập tức gọi nó sẽ là ký tự ít thanh lịch hơn để viết.


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

Tại sao không chỉ viết một trình bao bọc nhỏ: public T Exec<T>(Func<T> func) => return func();và sử dụng nó như thế này: int x = Exec(() => { return 1; });Điều đó với tôi đọc đẹp hơn rất nhiều so với việc đúc với tất cả các parens của nó.
mầm

@germi ý tưởng hay, nhưng nó cho tôi "Các đối số kiểu cho phương thức Exec không thể được suy ra từ cách sử dụng."
Glorfindel

@Glorfindel Bạn đã làm gì đó sai, sau đó: dotnetfiddle.net/oku7eX
canton7

@ canton7 vì tôi thực sự đang sử dụng lambda với tham số đầu vào ... Cảm ơn, nó hoạt động ngay bây giờ.
Glorfindel

Câu trả lời:


29

Thay vì cố gắng sử dụng lambda, tôi đề nghị bạn sử dụng một hàm trợ giúp nhỏ:

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

mà sau đó bạn có thể sử dụng như thế này : int x = Exec(myVar => myVar + 2, 0);. Điều này đọc cho tôi tốt hơn rất nhiều so với các lựa chọn thay thế được đề xuất ở đây.


25

Thật xấu xí, nhưng nó có thể:

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

Bạn có thể sử dụng, nhưng lambda cần được đặt trong ngoặc đơn.

Trên đây cũng có thể được đơn giản hóa:

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

2
À, tất nhiên rồi. Tôi chỉ thử int output = (Func<int>)(() => { return 1; })();nhưng diễn viên có mức độ ưu tiên thấp hơn so với thực hiện lambda.
Glorfindel

Nó vẫn không giải quyết được vấn đề không muốn viết tên lớp cực kỳ dài.
Glorfindel

4

Chữ Lambda trong C # có một sự phân biệt tò mò ở chỗ ý nghĩa của chúng phụ thuộc vào loại của chúng. Về cơ bản, chúng bị quá tải về kiểu trả về của chúng , một thứ không tồn tại ở bất kỳ nơi nào khác trong C #. (Chữ số có phần giống nhau.)

Chính xác nghĩa đen lambda có thể đánh giá một hàm ẩn danh mà bạn có thể thực thi (tức là a Func/ Action) hoặc biểu diễn trừu tượng các hoạt động bên trong Thân, giống như Cây Cú pháp Trừu tượng (tức là Cây Biểu thức LINQ).

Ví dụ, cách thứ hai là cách thức hoạt động của LINQ to SQL, LINQ to XML, v.v.: lambdas không đánh giá mã thực thi, họ đánh giá cho Cây biểu thức LINQ và sau đó nhà cung cấp LINQ có thể sử dụng các cây biểu thức đó để hiểu những gì phần thân của lambda đang thực hiện và tạo ví dụ: Truy vấn SQL từ đó.

Trong trường hợp của bạn, không có cách nào để trình biên dịch biết được nghĩa đen của lambda nghĩa là được đánh giá theo một Funchoặc một biểu thức LINQ. Đó là lý do tại sao câu trả lời của Johnathan Barclay hoạt động: nó đưa ra một kiểu cho biểu thức lambda và do đó, trình biên dịch biết rằng bạn muốn có một Funcmã được biên dịch thực thi phần thân của lambda thay vì Cây biểu thức LINQ không được đánh giá đại diện cho mã bên trong cơ thể của lambda.


3

Bạn có thể nội tuyến khai báo Funcbằng cách làm

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

và ngay lập tức gọi nó.


2

Bạn cũng có thể tạo bí danh trong Selectphương thức

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

hoặc với ??nhà điều hành

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

Nếu bạn không ngại vi phạm một vài nguyên tắc thiết kế phương thức mở rộng, các phương thức mở rộng kết hợp với toán tử ?.có điều kiện null có thể đưa bạn đi xa một cách hợp lý:

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

sẽ cung cấp cho bạn điều này:

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

và nếu bạn chủ yếu cần mảng, thì ghi đè ToArrayphương thức mở rộng để gói gọn một vài lệnh gọi phương thức khác:

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

dẫn đến:

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
        })
    })
};
Khi sử dụng trang web của chúng tôi, bạn xác nhận rằng bạn đã đọc và hiểu Chính sách cookieChính sách bảo mật của chúng tôi.
Licensed under cc by-sa 3.0 with attribution required.