Bagaimana cara mengembalikan nilai dengan metode anonim?

90

Ini gagal

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

dengan kesalahan

Tidak dapat mengonversi ekspresi lambda menjadi tipe 'string' karena ini bukan tipe delegasi

Apa arti kesalahan tersebut dan bagaimana cara mengatasinya?

4thSpace
sumber
Mengapa pertanyaan ini muncul pertama kali di Google saat menelusuri kesalahan "fungsi anonim yang diubah menjadi delegasi yang kembali kosong tidak dapat mengembalikan nilai" padahal jelas tidak ada hubungannya dengan itu?
Calmarius

Jawaban:

137

Masalahnya di sini adalah Anda telah menentukan metode anonim yang mengembalikan a stringtetapi mencoba untuk menetapkannya langsung ke string. Ini adalah ekspresi yang ketika dipanggil menghasilkan a stringitu tidak langsung a string. Ini harus ditetapkan ke jenis delegasi yang kompatibel. Dalam hal ini pilihan termudah adalahFunc<string>

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

Ini dapat dilakukan dalam satu baris dengan sedikit casting atau menggunakan konstruktor delegasi untuk menetapkan jenis lambda yang diikuti dengan pemanggilan.

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

Catatan: Kedua sampel dapat disingkat menjadi bentuk ekspresi yang tidak memiliki ekstensi { return ... }

Func<string> temp = () => "test";
string temp = ((Func<string>)(() => "test"))();
string temp = new Func<string>(() => "test")();
JaredPar
sumber
Terima kasih. Jadi tidak ada cara untuk melakukan semuanya dalam satu baris (termasuk menetapkan string)? Nilai yang saya inginkan ("test", yang sebenarnya merupakan variabel dalam kehidupan nyata) ada di dalam lambda lain jadi saya kehilangan ruang lingkup jika saya mencoba mendefinisikan seperti yang Anda miliki di atas.
4thSpace
@ 4thSpace itu dapat dilakukan dalam satu baris dengan beberapa pengecoran jahat. Saya memperbarui jawaban saya untuk menunjukkan jalan
JaredPar
Atau dalam kasus ini, adil Func<string> temp = () => "test";.
Gabe
Atau dalam kasus suntingan Anda,string temp = new Func<string>(() => "test")();
Gabe
Sempurna! Jika saya ingin memasukkan int, dapatkah Anda menunjukkannya dalam satu baris? Saya mencoba ini tetapi tidak berhasil: ((Func <int, string>) ((4) => {return "test";})) ();
4thSpace
15

Anda mencoba untuk menetapkan delegasi fungsi ke tipe string. Coba ini:

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

Sekarang Anda dapat menjalankan fungsi tersebut dengan cara berikut:

string s = temp();

Variabel "s" sekarang akan memiliki nilai "test".

Dave Swersky
sumber
1
Ini tidak dapat dikompilasi: "Tidak dapat menetapkan ekspresi lambda ke variabel lokal yang diketik secara implisit"
Dave Bish
@ Dave: Menarik, tidak tahu tentang batasan itu. Diperbarui, terima kasih!
Dave Swersky
8

Dengan menggunakan sedikit fungsi pembantu dan generik, Anda dapat membiarkan kompiler menyimpulkan jenisnya, dan memendekkannya sedikit:

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

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

Catatan samping: ini juga bagus karena Anda kemudian dapat mengembalikan tipe anonim:

var temp = FuncInvoke(()=>new {foo=1,bar=2});
joeriks
sumber
Teknik yang menarik. Apakah ini menambah run-time overhead, atau apakah semuanya pada waktu kompilasi?
ToolmakerSteve
@ToolmakerSteve: Dugaan saya adalah bahwa itu akan menambahkan sedikit overhead runtime yang teeeeensy (itu membungkus panggilan ke metode anonim di dalam metode lain) - namun, saya curiga itu juga akan tergantung di mana metode FuncInvoke didefinisikan (perakitan yang sama seperti di mana itu disebut vs rakitan berbeda dll), karena ini mungkin semacam hal yang kompilator dapat "sebaris". Ini adalah jenis pertanyaan yang dijawab dengan menulis program tes cepat, menyusun, dan kemudian memilah-milah IL yang dihasilkan.
Daniel Scott
@ToolmakerSteve Mengikuti dari "tebakan" terakhir pada dampak kinerja, saya akan menambahkan bahwa bahkan dampak terburuk yang akan terjadi pada kinerja akan hampir nihil (satu panggilan fungsi tambahan, ke metode statis non-virtual). Siapa pun yang menggunakan teknik ini kemungkinan besar melakukannya karena mereka melempar lambda. Itu berarti mereka mungkin menggunakan setidaknya beberapa metode ekstensi LINQ di suatu tempat, jadi kemungkinannya cukup besar bahwa mereka secara tidak sengaja merangkai beberapa metode LINQ bersama-sama dengan cara yang merusak kinerja 100.000 kali lebih buruk daripada satu panggilan fungsi tambahan ;)
Daniel Scott
6

Anda dapat menggunakan metode anonim dengan argumen:

int arg = 5;

string temp = ((Func<int, string>)((a) => { return a == 5 ? "correct" : "not correct"; }))(arg);
HamidReza
sumber
Bisa, tapi tolong jelaskan bagaimana ini menjadi jawaban atas pertanyaan tersebut.
ToolmakerSteve
3

Metode anonim dapat mengembalikan nilai menggunakan func delegate. Berikut adalah contoh di mana saya telah menunjukkan bagaimana mengembalikan nilai menggunakan metode anonim.

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();
        }
    }
}
Debendra Dash
sumber
0

Ini adalah contoh lain menggunakan C # 8 ( juga bisa bekerja dengan versi .NET lain yang mendukung tugas paralel )

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();
        }
    }
}
wbadry.dll
sumber