C # Meneruskan Fungsi sebagai Argumen [duplikat]

146

Saya telah menulis fungsi di C # yang melakukan diferensiasi numerik. Ini terlihat seperti ini:

public double Diff(double x)
{
    double h = 0.0000001;

    return (Function(x + h) - Function(x)) / h;
}

Saya ingin bisa meneruskan fungsi apa pun, seperti di:

public double Diff(double x, function f)
{
    double h = 0.0000001;

    return (f(x + h) - f(x)) / h;
}

Saya pikir ini mungkin dilakukan dengan delegasi (mungkin?) Tetapi saya tidak yakin bagaimana menggunakannya.

Bantuan apa pun akan sangat dihargai.

Abu
sumber
1
Apakah ini menjawab pertanyaan Anda? Lewati Metode sebagai Parameter menggunakan C #
Davide Cannizzo

Jawaban:

149

Menggunakan Func seperti yang disebutkan di atas berfungsi, tetapi ada juga delegasi yang melakukan tugas yang sama dan juga menentukan maksud dalam penamaan:

public delegate double MyFunction(double x);

public double Diff(double x, MyFunction f)
{
    double h = 0.0000001;

    return (f(x + h) - f(x)) / h;
}

public double MyFunctionMethod(double x)
{
    // Can add more complicated logic here
    return x + 10;
}

public void Client()
{
    double result = Diff(1.234, x => x * 456.1234);
    double secondResult = Diff(2.345, MyFunctionMethod);
}
Ian Johnson
sumber
5
Di 3.5 dan yang lebih baru, Func <> s dan delegates dapat dipertukarkan, dan itu berarti delegasi anonim dan lambda (yang merupakan gula sintaksis untuk delegasi anonim) juga dapat digunakan. Jadi tidak masalah apakah Anda menetapkan parameter sebagai Func <double, double> atau delegasi yang mengambil double dan mengembalikan double. Satu-satunya keuntungan nyata yang diberikan oleh seorang delegasi bernama adalah kemampuan untuk menambahkan komentar xml-doc; nama deskriptif semudah diimplementasikan seperti nama parameter daripada jenisnya.
KeithS
3
Saya berpendapat bahwa prototipe metode masih membuat kode lebih mudah dibaca daripada Func <x, y> - bukan hanya penamaan yang membuat kode dapat dibaca dan seperti yang Anda katakan itu tidak menghentikan Anda untuk meneruskan lambda ke dalam kode.
Ian Johnson
Jika penamaan tipe delegasi sangat penting untuk kejelasan kode, saya pikir dalam banyak kasus saya akan condong ke antarmuka dan implementasi.
Quentin-starin
@qstarin bukan hanya penamaan delegasi tetapi penamaan argumen yang harus diambil metode, terutama jika mereka hanya tipe asli. Anda benar, kebanyakan saya menggunakan antarmuka daripada delegasi
Ian Johnson
Jika saya menjadikan salah satunya sebagai fungsi umum yang menyediakan fungsionalitas umum, (dengan tipe pengembalian yang tidak terlalu umum) semuanya berfungsi sampai saya mencoba menggunakannya dengan void. Apakah saya benar-benar perlu membuat fungsi duplikat menggunakan Action daripada Func atau apakah ada cara yang lebih baik?
Dan Chase
179

Ada beberapa tipe umum dalam .Net (v2 dan yang lebih baru) yang membuat fungsi penerusan sebagai delegasi menjadi sangat mudah.

Untuk fungsi dengan tipe kembalian, ada Func <> dan untuk fungsi tanpa tipe kembalian ada Action <>.

Baik Func maupun Action dapat dideklarasikan untuk mengambil dari 0 hingga 4 parameter. Misalnya, Func <double, int> menggunakan satu double sebagai parameter dan mengembalikan sebuah int. Tindakan <double, double, double> menggunakan tiga double sebagai parameter dan tidak mengembalikan apa-apa (void).

Jadi Anda dapat mendeklarasikan fungsi Diff Anda untuk mengambil Func:

public double Diff(double x, Func<double, double> f) {
    double h = 0.0000001;

    return (f(x + h) - f(x)) / h;
}

Dan kemudian Anda menyebutnya begitu, cukup beri nama fungsi yang sesuai dengan tanda tangan Func atau Action Anda:

double result = Diff(myValue, Function);

Anda bahkan dapat menulis fungsi sesuai dengan sintaks lambda:

double result = Diff(myValue, d => Math.Sqrt(d * 3.14));
quentin-starin
sumber
30
Dalam .NET 4, keduanya Funcdan Actiontelah diperbarui untuk memungkinkan hingga 16 parameter.
Joel Mueller
5
Hal yang sangat keren untuk dilakukan adalah mengembalikan Func<double, double>yang merupakan turunan pertama dari fungsi input, tentu saja dihitung secara numerik. return x => (f(x + h) - f(x)) / h;Anda bahkan dapat menulis kelebihan beban yang mengembalikan nturunan ke - th dari fungsi masukan.
Ani
16
public static T Runner<T>(Func<T> funcToRun)
{
    //Do stuff before running function as normal
    return funcToRun();
}

Pemakaian:

var ReturnValue = Runner(() => GetUser(99));
kravits88
sumber
Saya penasaran, mengapa parameter tipe generik?
kdbanman
2
Saya mendapatkan kesalahan ini: Argumen tipe untuk metode 'Runner <T> (Func <T>)' tidak dapat disimpulkan dari penggunaan. Coba tentukan argumen tipe secara eksplisit.
DermFrench
Apa tanda tangan metode "funcToRun" Anda?
kravits88