Mengapa kompilasi OK, ketika saya menggunakan metode Invoke, dan tidak OK ketika saya mengembalikan Func <int, int> secara langsung?

28

Saya tidak mengerti kasus ini:

public delegate int test(int i);

public test Success()
{
    Func<int, int> f = x => x;
    return f.Invoke; // <- code successfully compiled 
}

public test Fail()
{
    Func<int, int> f = x => x;
    return f; // <- code doesn't compile
}

Mengapa kompilasi OK ketika saya menggunakan Invokemetode dan tidak OK ketika saya kembali csharp Func<int,int>secara langsung?

Evgeniy Terekhin
sumber
Anda memiliki delegasi yang berarti Anda mendapatkan semacam acara. Invoke mencegah pengecualian cross-thread dan memungkinkan beberapa proses mengakses objek.
jdweng
Perhatikan bahwa Anda akan melihat masalah ini bahkan jika Anda menggunakan dua delegasi yang tampak identik seperti delegate void test1(int i);dandelegate void test2(int i);
Matthew Watson

Jawaban:

27

Ada dua hal yang perlu Anda ketahui untuk memahami perilaku ini.

  1. Semua delegasi berasal System.Delegate, tetapi delegasi yang berbeda memiliki tipe yang berbeda dan karenanya tidak dapat ditugaskan satu sama lain.
  2. Bahasa C # menyediakan penanganan khusus untuk menetapkan metode atau lambda ke delegasi .

Karena delegasi yang berbeda memiliki tipe yang berbeda, itu berarti Anda tidak dapat menetapkan delegasi dari satu tipe ke yang lain.

Misalnya, diberikan:

delegate void test1(int i);
delegate void test2(int i);

Kemudian:

test1 a = Console.WriteLine; // Using special delegate initialisation handling.
test2 b = a;                 // Using normal assignment, therefore does not compile.

Baris pertama di atas mengkompilasi OK karena menggunakan penanganan khusus untuk menetapkan lambda atau metode untuk delegasi.

Bahkan, baris ini ditulis ulang secara efektif seperti ini oleh kompiler:

test1 a = new test1(Console.WriteLine);

Baris kedua di atas tidak dikompilasi karena sedang mencoba untuk menetapkan contoh dari satu jenis ke jenis lain yang tidak kompatibel.

Sejauh ini, tidak ada tugas yang kompatibel antara test1dantest2 karena mereka adalah tipe yang berbeda.

Jika membantu untuk memikirkannya, pertimbangkan hierarki kelas ini:

class Base
{
}

class Test1 : Base
{
}

class Test2 : Base
{
}

Kode berikut TIDAK akan dikompilasi, meskipun Test1dan Test2berasal dari kelas dasar yang sama:

Test1 test1 = new Test1();
Test2 test2 = test1; // Compile error.

Ini menjelaskan mengapa Anda tidak dapat menetapkan satu tipe delegasi ke yang lain. Itu hanya bahasa C # yang normal.

Namun, yang penting adalah untuk memahami mengapa Anda diizinkan untuk menetapkan metode atau lambda ke delegasi yang kompatibel. Seperti disebutkan di atas, ini adalah bagian dari dukungan bahasa C # untuk delegasi.

Jadi akhirnya untuk menjawab pertanyaan Anda:

Saat Anda menggunakan, Invoke()Anda menetapkan panggilan METHOD ke delegasi menggunakan penanganan bahasa C # khusus untuk menetapkan metode atau lambdas ke delegasi daripada mencoba untuk menetapkan tipe yang tidak kompatibel - karenanya dikompilasi OK.

Agar benar-benar jelas, kode yang dikompilasi dalam OP Anda:

public test Success()
{
    Func<int, int> f = x => x;
    return f.Invoke; // <- code successfully compiled 
}

Sebenarnya dikonversi secara konseptual menjadi sesuatu seperti:

public test Success()
{
    Func<int, int> f = x => x;
    return new test(f.Invoke);
}

Sedangkan kode gagal berusaha untuk menetapkan antara dua jenis yang tidak kompatibel:

public test Fail()
{
    Func<int, int> f = x => x;
    return f; // Attempting to assign one delegate type to another: Fails
}
Matthew Watson
sumber
6

Dalam kasus kedua, fadalah tipe Func<int, int>, tetapi metode ini dikatakan mengembalikan a test. Ini adalah tipe yang tidak terkait (mendelegasikan), yang tidak dapat dipertukarkan satu sama lain, sehingga terjadi kesalahan kompiler. Anda dapat pergi ke bagian spesifikasi bahasa ini, dan mencari "delegate". Anda tidak akan menemukan penyebutan konversi antara delegasi yang memiliki tanda tangan yang sama.

Namun dalam kasus pertama, f.Invokeadalah ekspresi grup metode , yang sebenarnya tidak memiliki tipe. Compiler C # akan mengonversi ekspresi grup metode ke tipe delegasi tertentu sesuai dengan konteksnya, melalui konversi grup metode .

(Mengutip peluru ke-5 di sini , penekanan milikku)

Ekspresi diklasifikasikan sebagai salah satu dari yang berikut:

  • ...

  • Grup metode, yang merupakan kumpulan metode kelebihan beban yang dihasilkan dari pencarian anggota. [...] Grup metode diizinkan dalam invocation_expression, delegate_creation_expression dan sebagai sisi kiri isoperator, dan dapat secara implisit dikonversi ke tipe delegasi yang kompatibel.

Dalam hal ini, itu dikonversi ke testtipe delegasi.

Dengan kata lain, return ftidak berfungsi karena fsudah memiliki tipe, tetapi f.Invokebelum memiliki tipe.

Penyapu
sumber
2

Masalah di sini adalah kompatibilitas Tipe:

Berikut ini adalah definisi delegasi Func dari Sumber MSDN:

public delegate TResult Func<in T, out TResult>(T arg);

Jika Anda melihat tidak ada hubungan langsung antara Fungsi yang disebutkan di atas dan Delegasi yang Anda tentukan:

public delegate int test(int i);

Mengapa cuplikan pertama mengkompilasi:

public test Success()
{
    Func<int, int> f = x => x;
    return f.Invoke; // <- code successfully compiled 
 }

Delegasi dibandingkan menggunakan tanda tangan, yang merupakan parameter input dan hasil Output, akhirnya seorang Delegasi adalah pointer Fungsi dan dua fungsi dapat dibandingkan hanya melalui tanda tangan. Pada saat runtime, metode yang dipanggil melalui Func ditugaskan ke Testdelegasi, karena Signature sama berfungsi dengan mulus. Ini adalah penunjuk fungsi, di mana Testdelegasi sekarang akan memanggil metode yang ditunjuk oleh delegasi Func

Mengapa Cuplikan ke-2 gagal dikompilasi

Antara Func dan delegasi uji, tidak ada kompatibilitas jenis / penugasan, Func tidak dapat mengisi sebagai bagian dari aturan sistem Type. Bahkan ketika hasilnya dapat ditugaskan dan diisi test delegateseperti yang dilakukan pada kasus pertama.

Mrinal Kamboj
sumber