ฟังก์ชั่นการส่ง C # เป็นอาร์กิวเมนต์


141

ฉันเขียนฟังก์ชันใน C # ที่ทำให้เกิดความแตกต่างเชิงตัวเลข ดูเหมือนว่านี้:

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

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

ฉันต้องการที่จะผ่านในฟังก์ชั่นใด ๆ เช่นเดียวกับใน:

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

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

ฉันคิดว่ามันเป็นไปได้กับผู้ได้รับมอบหมาย (อาจจะ?) แต่ฉันไม่แน่ใจว่าจะใช้มันอย่างไร

ความช่วยเหลือใด ๆ ที่จะได้รับการชื่นชมอย่างมาก.

คำตอบ:


146

การใช้ Func ดังกล่าวข้างต้นทำงานได้ แต่ยังมีผู้ได้รับมอบหมายที่ทำงานเดียวกันและยังกำหนดเจตนาภายในการตั้งชื่อ:

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);
}

5
ใน 3.5 และใหม่กว่านั้น Func <> s และผู้ได้รับมอบหมายสามารถใช้แทนกันได้และนั่นหมายความว่าผู้ได้รับมอบหมายที่ไม่ระบุชื่อและ lambdas (ซึ่งเป็นน้ำตาล syntactic สำหรับผู้ได้รับมอบหมายที่ไม่ระบุชื่อ) สามารถใช้ ดังนั้นจึงไม่สำคัญว่าคุณจะระบุพารามิเตอร์เป็น Func <double, double> หรือผู้รับมอบสิทธิ์ที่รับ double และคืน double ข้อได้เปรียบที่แท้จริงเพียงอย่างเดียวที่มอบสิทธิ์ให้คุณคือความสามารถในการเพิ่มความคิดเห็น xml-doc ชื่อที่สื่อความหมายนั้นง่ายต่อการนำมาใช้เป็นชื่อพารามิเตอร์แทนที่จะเป็นชนิด
KeithS

3
ฉันจะยืนยันว่าต้นแบบวิธีการยังคงทำให้โค้ดอ่านง่ายกว่า Func <x, y> - มันไม่ใช่แค่การตั้งชื่อที่ทำให้อ่านโค้ดได้
เอียนจอห์นสัน

หากการตั้งชื่อประเภทตัวแทนมีความสำคัญต่อความชัดเจนของรหัสฉันคิดว่าในกรณีส่วนใหญ่ฉันจะเอนไปยังส่วนต่อประสานและการนำไปใช้งาน
quentin-starin

@qstarin มันไม่ได้เป็นเพียงการตั้งชื่อของผู้รับมอบสิทธิ์ แต่การตั้งชื่อของอาร์กิวเมนต์ที่วิธีการจะต้องใช้โดยเฉพาะอย่างยิ่งถ้าพวกเขาเป็นเพียงประเภทพื้นเมือง คุณพูดถูกฉันส่วนใหญ่ใช้อินเทอร์เฟซแทนผู้รับมอบสิทธิ์
Ian Johnson

หากฉันสร้างหนึ่งในนี้เป็นฟังก์ชั่นทั่วไปที่ให้ฟังก์ชั่นทั่วไปทุกอย่างทำงานได้ (กับประเภทที่ไม่เหมือนกัน) จะใช้งานได้จนกว่าฉันจะลองใช้กับโมฆะ ฉันจำเป็นต้องสร้างฟังก์ชันที่ซ้ำกันโดยใช้ Action แทนที่จะเป็น Func หรือมีวิธีที่ดีกว่านี้หรือไม่?
Dan Chase

175

มีประเภททั่วไปสองสามประเภทใน. Net (v2 และใหม่กว่า) ที่ทำให้ฟังก์ชันการส่งผ่านเป็นไปอย่างง่ายดาย

สำหรับฟังก์ชั่นที่มีประเภทส่งคืนจะมี Func <> และสำหรับฟังก์ชั่นที่ไม่มีประเภทส่งคืนจะมี Action <>

ทั้ง Func และ Action สามารถประกาศให้รับได้ตั้งแต่ 0 ถึง 4 พารามิเตอร์ ตัวอย่างเช่น Func <double, int> รับหนึ่งคู่เป็นพารามิเตอร์และส่งกลับค่า int การดำเนินการ <double, double, double> ใช้เวลาสาม doubles เป็นพารามิเตอร์และส่งคืนค่าใด ๆ (void)

ดังนั้นคุณสามารถประกาศฟังก์ชั่น Diff เพื่อใช้ Func:

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

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

จากนั้นคุณเรียกมันว่าเป็นเช่นนั้นเพียงแค่ให้ชื่อของฟังก์ชั่นที่เหมาะกับลายเซ็นของ Func หรือ Action ของคุณ:

double result = Diff(myValue, Function);

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

double result = Diff(myValue, d => Math.Sqrt(d * 3.14));

29
ใน. NET 4 ทั้งสองFuncและActionได้รับการปรับปรุงเพื่ออนุญาตให้ใช้พารามิเตอร์มากถึง 16พารามิเตอร์
Joel Mueller

5
สิ่งที่เจ๋งจริงๆที่ต้องทำคือคืนค่าFunc<double, double>นั่นคืออนุพันธ์อันดับหนึ่งของฟังก์ชันอินพุตซึ่งคำนวณจากตัวเลขแน่นอน return x => (f(x + h) - f(x)) / h;คุณสามารถเขียน overload ที่ส่งกลับnอนุพันธ์ของฟังก์ชันอินพุตได้
Ani

15
public static T Runner<T>(Func<T> funcToRun)
{
    //Do stuff before running function as normal
    return funcToRun();
}

การใช้งาน:

var ReturnValue = Runner(() => GetUser(99));

ฉันอยากรู้ว่าทำไมพารามิเตอร์ประเภททั่วไป?
kdbanman

2
ฉันได้รับข้อผิดพลาดนี้: อาร์กิวเมนต์ประเภทของวิธี 'Runner <T> (Func <T>)' ไม่สามารถอนุมานได้จากการใช้งาน ลองระบุอาร์กิวเมนต์ประเภทอย่างชัดเจน
DermFrench

ลายเซ็นวิธีการ "funcToRun" ของคุณคืออะไร
kravits88
โดยการใช้ไซต์ของเรา หมายความว่าคุณได้อ่านและทำความเข้าใจนโยบายคุกกี้และนโยบายความเป็นส่วนตัวของเราแล้ว
Licensed under cc by-sa 3.0 with attribution required.