Mengapa properti tidak secara implisit dapat dikonversi menjadi delegasi

9

Kita semua tahu bahwa properti di C # dikompilasi dengan metode lama sebenarnya. Tetapi tidak seperti metode (-group), mereka tidak dapat diberikan sebagai argumen untuk metode lain yang mengharapkan delegasi seperti Func<T>atau Action<T>(pengambil dan penyetel). Apakah ada sesuatu yang khusus melarang ini, atau itu hanya fitur yang tidak datang "secara gratis" ketika membuat grup metode secara implisit dapat dikonversi menjadi delegasi?

Kode contoh:

public class MyClass
{
    public static int X { get; set; }
}

private static void Foo<T>(Func<T> f)
{
    Console.WriteLine(f());
}

private static void Bar<T>(Action<T> f)
{
    f(default(T));
}

private static void Test()
{
    // neither of these lines compile, even with an explicit type-cast.
    Foo(MyClass.X);
    Bar(MyClass.X);
}

Jika saya harus menebak, saya akan mengatakan bahwa masalah sintaksis yang membedakan antara pemanggilan dan referensi ke properti itu sendiri tidak layak untuk diselesaikan ketika kita bisa melakukan sedikit saja penulisan () => MyClass.Xatau x => MyClass.X = x.

sara
sumber
Mereka akan memanggil apa? Dapatkan atau atur
Ewan
Pikir saya akan tergantung pada jenis delegasi. setadalah Actiondan getmerupakan Funcmisalnya
sara
1
FYI, sintaks menggunakan delegasi inline untuk lulus pengambil / penyetel . delegate {return SomeProperty}melewati pengambil; delegate(YourType value) {SomeProperty = value}melewati setter. Jika Anda hanya perlu melewati satu atau dua tempat, ini adalah singkatan untuk metode terbungkus yang seharusnya Anda buat.
ToolmakerSteve
Dan FWIW, jika Anda menemukan diri Anda ingin untuk melewati kedua getter dan setter, pertimbangkan bukan mendefinisikan sebuah Interfacedengan properti itu, dan mengubah tanda tangan dari metode yang disebut untuk mengambil bahwa antarmuka sebagai parameter.
ToolmakerSteve

Jawaban:

7

Masalahnya adalah "MyClass.X" memiliki makna yang jelas, yaitu memanggil pengambil pada properti "X". Yang penting untuk dicatat adalah bahwa, meskipun suatu metode sedang dipanggil, tanda kurung tidak diperlukan.

Di sisi lain, saat memanggil metode biasa, seperti ketika Anda menelepon "Foo" dalam kode contoh Anda, tanda kurung yang diperlukan untuk menunjukkan bahwa metode harus dieksekusi. Jika Anda ingin memberikan referensi ke suatu metode maka Anda akan mengecualikan tanda kurung (lihat contoh di bawah).

Ini berarti bahwa tidak ada ambiguitas ketika mereferensikan suatu metode - baik Anda mengeksekusinya segera (melewati argumen apa pun yang diperlukan dan termasuk tanda kurung) atau Anda meneruskan metode tersebut sebagai argumen (tanpa tanda kurung).

Dengan pengambil properti atau penyetel, tidak ada tanda kurung berarti "segera jalankan pengambil / penyetel". Jika pengambil properti / penyetel juga bisa diteruskan sebagai grup metode maka terkadang "x.Name" akan berarti "jalankan pengambil Nama sekarang" dan kadang-kadang itu berarti "lulus pengambil Nama sebagai grup metode". Meskipun dimungkinkan untuk mengubah kompiler menjadi disambiguasi berdasarkan apakah "x.Name" tampaknya diteruskan ke fungsi yang mengharapkan string (dalam hal ini pengambil akan dieksekusi) atau apakah tampaknya akan masuk ke dalam fungsi yang mengharapkan Func <string> (dalam hal ini pengambil akan disahkan sebagai grup metode), tim bahasa C # mencoba untuk menghindari skenario yang berpotensi membingungkan ini.

using System;

namespace Example
{
    public static class Program
    {
        static void Main()
        {
            ValueWriter(1); // Execute ValueWriter (because brackets)
            Bar(ValueWriter); // Pass ValueWriter as an action (no brackets)
        }

        private static void Bar(Action<int> f)
        {
            f(2);
        }

        private static void ValueWriter(int value)
        {
            Console.WriteLine("Value is " + value);
        }
    }
}
Dan Roberts
sumber
5

Itu tidak mungkin karena dasar-dasar bagaimana tata bahasa dan kompiler bekerja.

Ekspresi dievaluasi "bottom up", yang berarti ekspresi MyClass.Xmengevaluasi nilai int sebelum hasil ini dimasukkan sebagai argumen fungsi. Proposal Anda tampaknya menyarankan bahwa konteks - jenis parameter - dapat mengarahkan jika ekspresi harus ditafsirkan sebagai permohonan pengambil atau sebagai penyempurnaan terhadap metode pengambil itu sendiri. Ini tidak mungkin dilakukan dengan cara yang tidak ambigu. Bagaimana jika jenis properti itu sendiri adalah a Funcatau Action? Dan bagaimana akan var x = MyClass.Xditafsirkan? Bagaimana jika metode ini memiliki overloads dengan baik intatau Funcatau Actionparameter?

Untuk menyelesaikan ambiguitas ini, Anda memerlukan perbedaan di tingkat sintaksis, mis. Operator dengan dukungan khusus dalam tata bahasa seperti typeofoperator, mis:

Foo(getterof(MyClass.X));

Tapi ini akan jauh merepotkan untuk fitur dengan manfaat terbatas.

JacquesB
sumber
Ya, kehalusan seseorang dapat bekerja dengan, jika harus, sementara ketidakpastian adalah show-stopper.
Deduplicator
2

Foo.Do()artinya doa dan Foo.Domenjadi, delegasi baik-baik saja.

Foo.Xmenjadi properti serta delegasi pengambil dan delegasi pengambil tergantung pada perbedaan konteks yang halus membingungkan. Fitur bagus yang tidak terlalu bagus bahkan tidak masuk ke C #, ini sepertinya yang buruk. Jika Anda ingin menulis singkat setelah kode coba Perl.

Sudah ada cara untuk menyatakan dengan jelas mana yang Anda butuhkan di C #, saya tidak melihat masalah.

Nathan Cooper
sumber