วิธีการคืนค่าด้วยวิธีการไม่ระบุชื่อ?


90

สิ่งนี้ล้มเหลว

string temp = () => {return "test";};

ด้วยข้อผิดพลาด

ไม่สามารถแปลงนิพจน์แลมบ์ดาเป็นพิมพ์ 'สตริง' เนื่องจากไม่ใช่ประเภทผู้รับมอบสิทธิ์

ข้อผิดพลาดหมายถึงอะไรและฉันจะแก้ไขได้อย่างไร


เหตุใดคำถามนี้จึงเป็นผลลัพธ์แรกใน Google เมื่อค้นหาข้อผิดพลาด "ฟังก์ชันที่ไม่ระบุตัวตนที่แปลงเป็นผู้รับมอบสิทธิ์ที่ส่งคืนเป็นโมฆะไม่สามารถคืนค่าได้" ในเมื่อเห็นได้ชัดว่าไม่มีส่วนเกี่ยวข้องใด ๆ
Calmarius

คำตอบ:


137

ปัญหาที่นี่คือคุณได้กำหนดวิธีการแบบไม่ระบุตัวตนซึ่งส่งคืน a stringแต่กำลังพยายามกำหนดให้กับไฟล์string. มันเป็นเรื่องการแสดงออกซึ่งเมื่อเรียกผลิตก็ไม่ได้โดยตรงstring stringจำเป็นต้องกำหนดให้กับประเภทผู้รับมอบสิทธิ์ที่เข้ากันได้ ในกรณีนี้ทางเลือกที่ง่ายที่สุดคือFunc<string>

Func<string> temp = () => {return "test";};

สิ่งนี้สามารถทำได้ในหนึ่งบรรทัดโดยการแคสต์เล็กน้อยหรือใช้คอนสตรัคเตอร์ที่ได้รับมอบหมายเพื่อสร้างประเภทของแลมบ์ดาตามด้วยการเรียกใช้

string temp = ((Func<string>)(() => { return "test"; }))();
string temp = new Func<string>(() => { return "test"; })();

หมายเหตุ: ตัวอย่างทั้งสองสามารถย่อให้อยู่ในรูปแบบนิพจน์ที่ไม่มี { return ... }

Func<string> temp = () => "test";
string temp = ((Func<string>)(() => "test"))();
string temp = new Func<string>(() => "test")();

ขอบคุณ. ไม่มีวิธีทำทุกอย่างในบรรทัดเดียว (รวมถึงการกำหนดสตริง)? ค่าที่ฉันต้องการ ("test" ซึ่งเป็นตัวแปรในชีวิตจริง) อยู่ในแลมบ์ดาอื่นดังนั้นฉันจึงหลวมขอบเขตถ้าฉันพยายามกำหนดตามที่คุณมีข้างต้น
4thSpace

@ 4thSpace สามารถทำได้ในบรรทัดเดียวด้วยการหล่อแบบชั่วร้าย ฉันอัปเดตคำตอบเพื่อแสดงวิธี
JaredPar

หรือในกรณีนี้ก็แค่Func<string> temp = () => "test";.
Gabe

หรือในกรณีของการแก้ไขของคุณstring temp = new Func<string>(() => "test")();
Gabe

สมบูรณ์แบบ! ถ้าฉันต้องการส่งผ่านเป็น int คุณสามารถแสดงในบรรทัดเดียวได้ไหม ฉันลองแล้ว แต่ไม่ไป: ((Func <int, string>) ((4) => {return "test";})) ();
4thSpace

15

คุณกำลังพยายามกำหนดฟังก์ชันที่มอบสิทธิ์ให้กับประเภทสตริง ลองสิ่งนี้:

Func<string> temp = () => {return "test";};

ตอนนี้คุณสามารถเรียกใช้ฟังก์ชันได้แล้ว:

string s = temp();

ตอนนี้ตัวแปร "s" จะมีค่าเป็น "test"


1
สิ่งนี้ไม่ได้รวบรวม: "ไม่สามารถกำหนดนิพจน์แลมบ์ดาให้กับตัวแปรโลคัลที่พิมพ์โดยนัย"
Dave Bish

@ เดฟ: น่าสนใจไม่รู้เกี่ยวกับข้อ จำกัด นั้น อัปเดตแล้วขอบคุณ!
Dave Swersky

8

การใช้ฟังก์ชันตัวช่วยเล็กน้อยและข้อมูลทั่วไปคุณสามารถให้คอมไพเลอร์สรุปประเภทและย่อให้สั้นลงเล็กน้อย:

public static TOut FuncInvoke<TOut>(Func<TOut> func)
{
    return func();
}

var temp = FuncInvoke(()=>"test");

หมายเหตุด้านข้าง: สิ่งนี้ดีเช่นกันเมื่อคุณสามารถส่งคืนประเภทที่ไม่ระบุตัวตนได้:

var temp = FuncInvoke(()=>new {foo=1,bar=2});

เทคนิคที่น่าสนใจ สิ่งนี้เพิ่มค่าใช้จ่ายรันไทม์หรือทั้งหมดในเวลาคอมไพล์?
ToolmakerSteve

@ToolmakerSteve: ฉันเดาว่ามันจะเพิ่มค่าใช้จ่ายรันไทม์เล็กน้อย (มันเป็นการตัดการโทรไปยังเมธอดที่ไม่ระบุชื่อภายในวิธีอื่น) - อย่างไรก็ตามฉันสงสัยว่ามันจะขึ้นอยู่กับว่าเมธอด FuncInvoke ถูกกำหนดไว้ที่ใด (แอสเซมบลีเดียวกับที่ มันถูกเรียกว่า vs แอสเซมบลีที่แตกต่างกัน ฯลฯ ) เนื่องจากอาจเป็นสิ่งที่คอมไพเลอร์สามารถ "อินไลน์" ได้ นี่คือคำถามประเภทที่ผู้คนตอบโดยการเขียนโปรแกรมทดสอบอย่างรวดเร็วรวบรวมและแยก IL ที่เป็นผลลัพธ์ออกมา
Daniel Scott

@ToolmakerSteve ต่อจาก "การคาดเดา" ครั้งสุดท้ายที่ผลกระทบต่อประสิทธิภาพฉันจะเพิ่มว่าแม้ผลกระทบในกรณีที่เลวร้ายที่สุดนี้จะมีต่อประสิทธิภาพก็แทบจะไม่มี (การเรียกฟังก์ชันพิเศษหนึ่งครั้งไปยังวิธีที่ไม่ใช่เสมือนแบบคงที่) ใครก็ตามที่ใช้เทคนิคนี้มักจะทำเช่นนั้นเพราะพวกเขากำลังขว้างลูกแกะไปรอบ ๆ นั่นหมายความว่าพวกเขาอาจใช้วิธีการขยาย LINQ อย่างน้อยสองวิธีที่ใดที่หนึ่งดังนั้นโอกาสที่ดีพอสมควรที่พวกเขาได้ผูกมัดวิธี LINQ สองสามวิธีเข้าด้วยกันโดยไม่ได้ตั้งใจในลักษณะที่ทำให้ประสิทธิภาพแย่กว่าการเรียกใช้ฟังก์ชันพิเศษถึง 100,000 เท่า ;)
Daniel Scott

6

คุณสามารถใช้วิธีการไม่ระบุชื่อกับอาร์กิวเมนต์:

int arg = 5;

string temp = ((Func<int, string>)((a) => { return a == 5 ? "correct" : "not correct"; }))(arg);

คุณทำได้ แต่โปรดอธิบายว่านี่เป็นคำตอบสำหรับคำถามอย่างไร
ToolmakerSteve

3

วิธีการที่ไม่ระบุชื่อสามารถส่งคืนค่าโดยใช้ตัวแทน func นี่คือตัวอย่างที่ฉันได้แสดงวิธีการคืนค่าโดยใช้วิธีการไม่ระบุตัวตน

using System;
using System.Collections.Generic;
using System.Linq;
using System.Text;
using System.Threading.Tasks;

namespace ConsoleApp1
{
    class Program
    {


        static void Main(string[] args)
        {
            Func<int, int> del = delegate (int x)
              {
                  return x * x;

              };

            int p= del(4);
            Console.WriteLine(p);
            Console.ReadLine();
        }
    }
}

0

นี่เป็นอีกตัวอย่างหนึ่งที่ใช้C # 8 ( สามารถทำงานกับ. NET เวอร์ชันอื่นที่รองรับงานคู่ขนานได้เช่นกัน )

using System;
using System.Threading.Tasks;

namespace Exercise_1_Creating_and_Sharing_Tasks
{
    internal static class Program
    {
        private static int TextLength(object o)
        {
            Console.WriteLine($"Task with id {Task.CurrentId} processing object {o}");
            return o.ToString().Length;
        }

        private static void Main()
        {
            const string text1 = "Welcome";
            const string text2 = "Hello";

            var task1 = new Task<int>(() => TextLength(text1));
            task1.Start();

            var task2 = Task.Factory.StartNew(TextLength, text2);

            Console.WriteLine($"Length of '{text1}' is {task1.Result}");
            Console.WriteLine($"Length of '{text2}' is {task2.Result}");

            Console.WriteLine("Main program done");
            Console.ReadKey();
        }
    }
}
โดยการใช้ไซต์ของเรา หมายความว่าคุณได้อ่านและทำความเข้าใจนโยบายคุกกี้และนโยบายความเป็นส่วนตัวของเราแล้ว
Licensed under cc by-sa 3.0 with attribution required.