ข้อผิดพลาดในการเรียกคอมไพเลอร์ Ambiguous - เมธอดที่ไม่ระบุชื่อและกลุ่มวิธีการที่มี Func <> หรือ Action


102

ฉันมีสถานการณ์ที่ฉันต้องการใช้ไวยากรณ์กลุ่มวิธีแทนที่จะใช้วิธีการที่ไม่ระบุชื่อ (หรือไวยากรณ์แลมบ์ดา) สำหรับการเรียกใช้ฟังก์ชัน

ฟังก์ชั่นนี้มีสองโอเวอร์โหลดหนึ่งที่ใช้เวลาอีกอันหนึ่งใช้Actionเวลา a Func<string>.

ฉันสามารถเรียกสองโอเวอร์โหลดได้อย่างมีความสุขโดยใช้วิธีการที่ไม่ระบุชื่อ (หรือไวยากรณ์แลมบ์ดา) แต่ได้รับข้อผิดพลาดของคอมไพเลอร์ของการเรียกใช้ที่ไม่ชัดเจนหากฉันใช้ไวยากรณ์กลุ่มวิธีการ ฉันสามารถแก้ปัญหาได้โดยการแคสต์อย่างชัดเจนไปยังActionหรือFunc<string>แต่ไม่คิดว่าสิ่งนี้จะจำเป็น

ใครช่วยอธิบายได้ไหมว่าทำไมต้องมีการแคสต์อย่างชัดเจน

ตัวอย่างโค้ดด้านล่าง

class Program
{
    static void Main(string[] args)
    {
        ClassWithSimpleMethods classWithSimpleMethods = new ClassWithSimpleMethods();
        ClassWithDelegateMethods classWithDelegateMethods = new ClassWithDelegateMethods();

        // These both compile (lambda syntax)
        classWithDelegateMethods.Method(() => classWithSimpleMethods.GetString());
        classWithDelegateMethods.Method(() => classWithSimpleMethods.DoNothing());

        // These also compile (method group with explicit cast)
        classWithDelegateMethods.Method((Func<string>)classWithSimpleMethods.GetString);
        classWithDelegateMethods.Method((Action)classWithSimpleMethods.DoNothing);

        // These both error with "Ambiguous invocation" (method group)
        classWithDelegateMethods.Method(classWithSimpleMethods.GetString);
        classWithDelegateMethods.Method(classWithSimpleMethods.DoNothing);
    }
}

class ClassWithDelegateMethods
{
    public void Method(Func<string> func) { /* do something */ }
    public void Method(Action action) { /* do something */ }
}

class ClassWithSimpleMethods
{
    public string GetString() { return ""; }
    public void DoNothing() { }
}

อัปเดต C # 7.3

เป็นต่อ0xcde 's ความคิดเห็นด้านล่างบน 20 มีนาคม 2019 (เก้าปีหลังจากที่ผมโพสต์คำถามนี้!) รหัสนี้คอมไพล์เป็นของ C # 7.3 ขอบคุณไปยังผู้สมัครเกินที่ดีขึ้น


ฉันลองใช้รหัสของคุณแล้วและได้รับข้อผิดพลาดเกี่ยวกับเวลาคอมไพล์เพิ่มเติม: 'void test.ClassWithSimpleMethods.DoNothing ()' มีประเภทการส่งคืนที่ไม่ถูกต้อง (ซึ่งอยู่ในบรรทัดที่ 25 ซึ่งเป็นข้อผิดพลาดที่ไม่ชัดเจน)
Matt Ellen

@ แมท: ฉันเห็นข้อผิดพลาดนั้นเช่นกัน ข้อผิดพลาดที่ฉันอ้างถึงในโพสต์ของฉันคือปัญหาการรวบรวมที่ VS ไฮไลต์ก่อนที่คุณจะลองคอมไพล์แบบเต็ม
Richard Ev

1
อย่างไรก็ตามนี่เป็นคำถามที่ดี ฉันชอบอะไรก็ตามที่บังคับให้ฉันเป็นสเป็ค :)
Jon Skeet

1
โปรดทราบว่ารหัสตัวอย่างของคุณจะรวบรวมถ้าคุณใช้ C # 7.3 ( <LangVersion>7.3</LangVersion>) หรือขอบคุณต่อมาผู้สมัครเกินที่ดีขึ้น
0

คำตอบ:


97

ก่อนอื่นขอบอกว่าคำตอบของจอนนั้นถูกต้อง นี่เป็นหนึ่งในส่วนที่มีขนดกที่สุดของข้อมูลจำเพาะดังนั้นจอนจึงควรดำน้ำลงไปก่อน

อย่างที่สองขอบอกว่าบรรทัดนี้:

การแปลงโดยนัยมีอยู่จากกลุ่มวิธีการเป็นประเภทผู้รับมอบสิทธิ์ที่เข้ากันได้

(เน้นย้ำ) เป็นเรื่องที่ทำให้เข้าใจผิดอย่างมากและเป็นเรื่องที่โชคร้าย ฉันจะคุยกับ Mads เกี่ยวกับการลบคำว่า "เข้ากันได้" ที่นี่

สาเหตุที่ทำให้เข้าใจผิดและเป็นเรื่องโชคร้ายเพราะดูเหมือนว่านี่เป็นการเรียกร้องให้ส่วน 15.2 "ความเข้ากันได้ของตัวแทน" ส่วนที่ 15.2 อธิบายถึงความสัมพันธ์ที่เข้ากันได้ระหว่างวิธีการและประเภทผู้รับมอบสิทธิ์แต่นี่เป็นคำถามเกี่ยวกับความสามารถในการแปลงของกลุ่มวิธีการและประเภทผู้รับมอบสิทธิ์ซึ่งแตกต่างกัน

ตอนนี้เราได้รับสิ่งนั้นหมดแล้วเราสามารถดูหัวข้อ 6.6 ของข้อมูลจำเพาะและดูสิ่งที่เราได้รับ

การทำเช่นความละเอียดเกินที่เราจำเป็นต้องตรวจสอบครั้งแรกซึ่งทับถมเป็นผู้สมัครที่ใช้บังคับ ผู้สมัครสามารถใช้ได้หากอาร์กิวเมนต์ทั้งหมดสามารถแปลงเป็นประเภทพารามิเตอร์ที่เป็นทางการได้โดยปริยาย พิจารณาโปรแกรมเวอร์ชันที่เรียบง่ายนี้:

class Program
{
    delegate void D1();
    delegate string D2();
    static string X() { return null; }
    static void Y(D1 d1) {}
    static void Y(D2 d2) {}
    static void Main()
    {
        Y(X);
    }
}

ลองดูทีละบรรทัด

การแปลงโดยนัยมีอยู่จากกลุ่มวิธีการเป็นประเภทผู้รับมอบสิทธิ์ที่เข้ากันได้

ฉันได้พูดคุยกันแล้วว่าคำว่า "เข้ากันได้" นั้นโชคร้ายที่นี่ กำลังเดินทางไป. เราสงสัยว่าเมื่อทำการแก้ปัญหาโอเวอร์โหลดบน Y (X) เมธอดกลุ่ม X จะแปลงเป็น D1 หรือไม่? มันแปลงเป็น D2?

กำหนดประเภทผู้รับมอบสิทธิ์ D และนิพจน์ E ที่จัดเป็นกลุ่มเมธอดการแปลงโดยนัยจะมีอยู่จาก E เป็น D หาก E มีอย่างน้อยหนึ่งวิธีที่ใช้ได้ [... ] กับรายการอาร์กิวเมนต์ที่สร้างโดยใช้พารามิเตอร์ ประเภทและตัวปรับแต่งของ D ตามที่อธิบายไว้ต่อไปนี้

จนถึงตอนนี้ดีมาก X อาจมีเมธอดที่ใช้ได้กับรายการอาร์กิวเมนต์ของ D1 หรือ D2

แอปพลิเคชันเวลาคอมไพล์ของการแปลงจากกลุ่มเมธอด E ไปเป็นผู้รับมอบสิทธิ์ประเภท D อธิบายไว้ดังต่อไปนี้

บรรทัดนี้ไม่ได้บอกว่ามีอะไรน่าสนใจ

โปรดทราบว่าการมีอยู่ของการแปลงโดยนัยจาก E เป็น D ไม่ได้รับประกันว่าแอปพลิเคชันเวลาคอมไพล์ของการแปลงจะประสบความสำเร็จโดยไม่มีข้อผิดพลาด

เส้นนี้น่าหลงใหล หมายความว่ามีการแปลงโดยนัยที่มีอยู่ แต่อาจกลายเป็นข้อผิดพลาด! นี่เป็นกฎที่แปลกประหลาดของ C # หากต้องการพูดนอกเรื่องสักครู่นี่คือตัวอย่าง:

void Q(Expression<Func<string>> f){}
string M(int x) { ... }
...
int y = 123;
Q(()=>M(y++));

การดำเนินการเพิ่มเป็นสิ่งผิดกฎหมายในแผนภูมินิพจน์ อย่างไรก็ตามแลมด้ายังคงสามารถแปลงเป็นชนิดแผนภูมินิพจน์ได้แม้ว่าจะเคยใช้การแปลงมาแล้วก็ตาม แต่ก็เป็นข้อผิดพลาด! หลักการตรงนี้คือเราอาจต้องการเปลี่ยนกฎของสิ่งที่สามารถไปได้ในแผนภูมินิพจน์ในภายหลัง การเปลี่ยนแปลงกฎระเบียบเหล่านั้นไม่ควรเปลี่ยนกฎระบบการพิมพ์ เราต้องการที่จะบังคับให้คุณที่จะทำให้โปรแกรมของคุณที่ชัดเจนในขณะนี้เพื่อที่ว่าเมื่อเราเปลี่ยนกฎสำหรับต้นไม้แสดงออกในอนาคตที่จะทำให้พวกเขาดีกว่าเราไม่ได้แนะนำการเปลี่ยนแปลงที่จะหมดในความละเอียดเกินพิกัด

อย่างไรก็ตามนี่เป็นอีกตัวอย่างหนึ่งของกฎที่แปลกประหลาดเช่นนี้ การแปลงอาจมีขึ้นเพื่อวัตถุประสงค์ในการแก้ไขปัญหาเกินพิกัด แต่เป็นข้อผิดพลาดในการใช้งานจริง แม้ว่าในความเป็นจริงแล้วนั่นไม่ใช่สถานการณ์ที่เราอยู่ที่นี่

กำลังเดินทางไป:

วิธีการเดียว M ถูกเลือกให้สอดคล้องกับการเรียกใช้เมธอดของรูปแบบ E (A) [... ] รายการอาร์กิวเมนต์ A คือรายการของนิพจน์ซึ่งแต่ละรายการจัดเป็นตัวแปร [... ] ของพารามิเตอร์ที่เกี่ยวข้องในทางการ - พารามิเตอร์ - รายการของ D.

ตกลง. ดังนั้นเราจึงทำการแก้ปัญหาโอเวอร์โหลดบน X เทียบกับ D1 รายการพารามิเตอร์ที่เป็นทางการของ D1 ว่างเปล่าดังนั้นเราจึงทำการแก้ปัญหาโอเวอร์โหลดบน X () และ joy เราพบเมธอด "string X ()" ที่ใช้งานได้ ในทำนองเดียวกันรายการพารามิเตอร์ที่เป็นทางการของ D2 ว่างเปล่า อีกครั้งเราพบว่า "string X ()" เป็นวิธีการที่ใช้ได้เช่นกัน

หลักการที่นี่ก็คือการกำหนดวิธีการเปลี่ยนแปลงกลุ่มต้องเลือกวิธีการออกจากกลุ่มวิธีการใช้ความละเอียดเกินพิกัดและความละเอียดเกินจะไม่พิจารณาผลตอบแทนประเภท

หากอัลกอริทึม [... ] สร้างข้อผิดพลาดข้อผิดพลาดเวลาคอมไพล์จะเกิดขึ้น มิฉะนั้นอัลกอริทึมจะสร้างวิธีที่ดีที่สุดเพียงวิธีเดียวที่มีจำนวนพารามิเตอร์เท่ากับ D และถือว่าการแปลงมีอยู่

มีวิธีเดียวในกลุ่มวิธี X ดังนั้นจึงต้องดีที่สุด เราได้พิสูจน์แล้วว่าประสบความสำเร็จในการแปลงที่มีอยู่จาก X เพื่อ D1 และจาก X เพื่อ D2

ตอนนี้สายนี้เกี่ยวข้องหรือไม่?

เมธอด M ที่เลือกต้องเข้ากันได้กับผู้รับมอบสิทธิ์ประเภท D มิฉะนั้นจะเกิดข้อผิดพลาดเวลาคอมไพล์

จริงๆแล้วไม่ใช่ไม่ใช่ในโปรแกรมนี้ เราไม่เคยไปไกลถึงการเปิดใช้งานสายนี้ เพราะจำไว้ว่าสิ่งที่เรากำลังทำอยู่นี้คือการพยายามแก้ไขปัญหาโอเวอร์โหลดบน Y (X) เรามีผู้สมัครสองคน Y (D1) และ Y (D2) ใช้ได้ทั้งสองอย่าง แบบไหนดีกว่ากัน ? ไม่มีที่ไหนในข้อกำหนดที่เราอธิบายถึงความดีระหว่างการแปลงที่เป็นไปได้ทั้งสองนี้

ตอนนี้เราอาจโต้แย้งได้อย่างแน่นอนว่าการแปลงที่ถูกต้องนั้นดีกว่าการแปลงที่ก่อให้เกิดข้อผิดพลาด จากนั้นจะกล่าวได้อย่างมีประสิทธิภาพในกรณีนี้ว่าการแก้ปัญหาการโอเวอร์โหลดพิจารณาประเภทการส่งคืนซึ่งเป็นสิ่งที่เราต้องการหลีกเลี่ยง คำถามคือหลักการใดดีกว่า: (1) รักษาค่าคงที่ที่ความละเอียดเกินไม่พิจารณาประเภทการส่งคืนหรือ (2) พยายามเลือกการแปลงที่เรารู้ว่าจะใช้ได้ผลกับสิ่งที่เรารู้ว่าจะไม่?

นี่คือการเรียกร้องการตัดสิน ด้วยlambdasเราจะพิจารณาประเภทผลตอบแทนในการแปลงประเภทเหล่านี้ในหัวข้อ 7.4.3.3:

E เป็นฟังก์ชันที่ไม่ระบุชื่อ T1 และ T2 เป็นชนิดผู้รับมอบสิทธิ์หรือประเภทแผนภูมินิพจน์ที่มีรายการพารามิเตอร์เหมือนกันชนิดผลตอบแทนที่สรุปคือ X มีอยู่สำหรับ E ในบริบทของรายการพารามิเตอร์นั้นและการเก็บรักษาอย่างใดอย่างหนึ่งต่อไปนี้:

  • T1 มีผลตอบแทนประเภท Y1 และ T2 มีผลตอบแทนประเภท Y2 และการแปลงจาก X เป็น Y1 ดีกว่าการแปลงจาก X เป็น Y2

  • T1 มีประเภทการส่งคืน Y และ T2 จะคืนค่าเป็นโมฆะ

โชคไม่ดีที่การแปลงกลุ่มวิธีการและการแปลงแลมบ์ดาไม่สอดคล้องกันในแง่นี้ อย่างไรก็ตามฉันสามารถอยู่กับมันได้

อย่างไรก็ตามเราไม่มีกฎ "ดีกว่า" ในการตัดสินว่าการแปลงใดดีกว่า X เป็น D1 หรือ X เป็น D2 ดังนั้นเราจึงให้ข้อผิดพลาดที่ไม่ชัดเจนเกี่ยวกับความละเอียดของ Y (X)


8
การแคร็ก - ขอบคุณมากสำหรับทั้งคำตอบและ (หวังว่า) ผลลัพธ์จะได้รับการปรับปรุงในข้อมูลจำเพาะ :) โดยส่วนตัวแล้วฉันคิดว่ามันจะสมเหตุสมผลสำหรับความละเอียดเกินพิกัดที่จะคำนึงถึงประเภทผลตอบแทนสำหรับการแปลงกลุ่มวิธีการเพื่อให้พฤติกรรมง่ายขึ้น แต่ ฉันเข้าใจว่ามันจะทำเช่นนั้นด้วยต้นทุนของความสม่ำเสมอ (เช่นเดียวกับการอนุมานประเภททั่วไปที่ใช้กับการแปลงกลุ่มวิธีเมื่อมีเพียงวิธีเดียวในกลุ่มวิธีการดังที่ฉันคิดว่าเราเคยพูดถึงมาก่อน)
Jon Skeet

35

แก้ไข: ฉันคิดว่าฉันเข้าใจแล้ว

ในฐานะที่เป็น zinglon กล่าวว่ามันเป็นเพราะมีการแปลงนัยจากGetStringไปActionถึงแม้ว่าโปรแกรมประยุกต์รวบรวมเวลาที่จะล้มเหลว นี่คือบทนำของส่วน 6.6 โดยเน้นบางส่วน (ของฉัน):

การแปลงโดยนัย (§6.1) มีอยู่จากกลุ่มวิธีการ (§7.1) ไปเป็นประเภทผู้ร่วมประชุมที่เข้ากันได้ กำหนดประเภทผู้รับมอบสิทธิ์ D และนิพจน์ E ที่จัดเป็นกลุ่มเมธอดการแปลงโดยนัยจะมีอยู่จาก E เป็น D ถ้า E มีอย่างน้อยหนึ่งวิธีที่ใช้ได้ในรูปแบบปกติ (§7.4.3.1) กับรายการอาร์กิวเมนต์ที่สร้างขึ้น โดยใช้ประเภทพารามิเตอร์และตัวปรับแต่งของ Dตามที่อธิบายไว้ต่อไปนี้

ตอนนี้ฉันเริ่มสับสนกับประโยคแรกซึ่งพูดถึงการแปลงเป็นประเภทผู้รับมอบสิทธิ์ที่เข้ากันได้ Actionไม่ใช่ผู้รับมอบสิทธิ์ที่เข้ากันได้สำหรับวิธีการใด ๆ ในGetStringกลุ่มวิธีการ แต่GetString()วิธีนี้สามารถใช้ได้ในรูปแบบปกติกับรายการอาร์กิวเมนต์ที่สร้างขึ้นโดยใช้ประเภทพารามิเตอร์และตัวปรับแต่งของ D โปรดทราบว่าสิ่งนี้ไม่ได้พูดถึงประเภทการส่งคืนของ D. นั่นเป็นสาเหตุที่ทำให้สับสน ... เพราะจะตรวจสอบความเข้ากันได้ของผู้รับมอบสิทธิ์GetString()เมื่อใช้การแปลงเท่านั้นโดยไม่ตรวจสอบว่ามีอยู่จริงหรือไม่

ฉันคิดว่ามันเป็นคำแนะนำที่จะปล่อยให้สมการโอเวอร์โหลดสั้น ๆ และดูว่าความแตกต่างระหว่างการมีอยู่ของ Conversion กับความสามารถในการบังคับใช้นี้จะแสดงออกมาได้อย่างไร นี่คือตัวอย่างสั้น ๆ แต่สมบูรณ์:

using System;

class Program
{
    static void ActionMethod(Action action) {}
    static void IntMethod(int x) {}

    static string GetString() { return ""; }

    static void Main(string[] args)
    {
        IntMethod(GetString);
        ActionMethod(GetString);
    }
}

ไม่มีนิพจน์การเรียกใช้เมธอดในMainคอมไพล์ แต่ข้อความแสดงข้อผิดพลาดแตกต่างกัน นี่คือสิ่งสำหรับIntMethod(GetString):

Test.cs (12,9): ข้อผิดพลาด CS1502: เมธอดโอเวอร์โหลดที่ดีที่สุดตรงกับ 'Program.IntMethod (int)' มีอาร์กิวเมนต์ที่ไม่ถูกต้อง

กล่าวอีกนัยหนึ่งส่วน 7.4.3.1 ของข้อมูลจำเพาะไม่พบสมาชิกฟังก์ชันที่เกี่ยวข้อง

นี่คือข้อผิดพลาดสำหรับActionMethod(GetString):

Test.cs (13,22): ข้อผิดพลาด CS0407: 'string Program.GetString ()' มีประเภทการส่งคืนที่ไม่ถูกต้อง

คราวนี้ได้ผลตามวิธีการที่ต้องการโทร - แต่ล้มเหลวในการแปลงที่ต้องการ น่าเสียดายที่ฉันไม่พบบิตของข้อมูลจำเพาะที่ดำเนินการตรวจสอบขั้นสุดท้าย - ดูเหมือนว่าอาจเป็น 7.5.5.1 แต่ฉันไม่เห็นว่าตรงไหน


คำตอบเก่าถูกลบออกยกเว้นบิตนี้ - เพราะฉันคาดหวังว่าเอริคจะให้ความกระจ่างกับ "ทำไม" ของคำถามนี้ ...

ยังคงมองหา ... ในเวลานั้นถ้าเราพูดว่า "Eric Lippert" สามครั้งคุณคิดว่าเราจะได้รับการเยี่ยมชม (และได้รับคำตอบ) หรือไม่?


@ จอน - เป็นไปได้ไหมclassWithSimpleMethods.GetStringและclassWithSimpleMethods.DoNothingไม่ใช่ตัวแทน?
Daniel

@ แดเนียล: ไม่ - นิพจน์เหล่านั้นเป็นนิพจน์กลุ่มเมธอดและเมธอดที่โอเวอร์โหลดควรจะใช้ได้ก็ต่อเมื่อมีการแปลงโดยนัยจากกลุ่มเมธอดไปเป็นประเภทพารามิเตอร์ที่เกี่ยวข้อง ดูส่วน 7.4.3.1 ของข้อมูลจำเพาะ
Jon Skeet

เมื่ออ่านหัวข้อ 6.6 ดูเหมือนว่าการแปลงจาก classWithSimpleMethods.GetString to Action จะถือว่ามีอยู่เนื่องจากรายการพารามิเตอร์เข้ากันได้ แต่การแปลง (หากพยายาม) ล้มเหลวในเวลาคอมไพล์ ดังนั้นการแปลงโดยนัยจึงมีอยู่สำหรับทั้งประเภทผู้รับมอบสิทธิ์และการเรียกนั้นไม่ชัดเจน
zinglon

@zinglon: คุณอ่าน§6.6เพื่อตรวจสอบว่าการแปลงจากClassWithSimpleMethods.GetStringถึงActionเป็นอย่างไร? สำหรับวิธีการMที่เข้ากันได้กับประเภทผู้รับมอบสิทธิ์D(§15.2) "การแปลงข้อมูลประจำตัวหรือการอ้างอิงโดยนัยมีอยู่จากประเภทMการส่งคืนเป็นประเภทการส่งคืนของD"
สัน

@ สัน: ข้อมูลจำเพาะไม่ได้บอกว่าแปลงที่ถูกต้องก็บอกว่ามันมีอยู่ อันที่จริงมันไม่ถูกต้องเนื่องจากล้มเหลวในเวลาคอมไพล์ สองจุดแรกของ§6.6เป็นตัวกำหนดว่ามีการแปลงหรือไม่ ประเด็นต่อไปนี้เป็นตัวกำหนดว่าการแปลงจะสำเร็จหรือไม่ จากจุดที่ 2: "มิฉะนั้นอัลกอริทึมจะสร้างวิธีที่ดีที่สุดเพียงวิธีเดียวที่มีพารามิเตอร์จำนวนเท่ากันกับ D และถือว่าการแปลงมีอยู่" §15.2ถูกเรียกในจุดที่ 3
zinglon

1

การใช้Func<string>และAction<string>(เห็นได้ชัดว่าแตกต่างกันมากกับActionและFunc<string>) ในการClassWithDelegateMethodsลบความคลุมเครือ

กำกวมยังเกิดขึ้นระหว่างและActionFunc<int>

ฉันยังได้รับข้อผิดพลาดด้านความคลุมเครือด้วยสิ่งนี้:

class Program
{ 
    static void Main(string[] args) 
    { 
        ClassWithSimpleMethods classWithSimpleMethods = new ClassWithSimpleMethods(); 
        ClassWithDelegateMethods classWithDelegateMethods = new ClassWithDelegateMethods(); 

        classWithDelegateMethods.Method(classWithSimpleMethods.GetOne);
    } 
} 

class ClassWithDelegateMethods 
{ 
    public void Method(Func<int> func) { /* do something */ }
    public void Method(Func<string> func) { /* do something */ } 
}

class ClassWithSimpleMethods 
{ 
    public string GetString() { return ""; } 
    public int GetOne() { return 1; }
} 

การทดลองเพิ่มเติมแสดงให้เห็นว่าเมื่อส่งผ่านกลุ่มวิธีการด้วยตนเองประเภทการส่งคืนจะถูกละเว้นโดยสิ้นเชิงเมื่อพิจารณาว่าจะใช้โอเวอร์โหลดใด

class Program
{
    static void Main(string[] args)
    {
        ClassWithSimpleMethods classWithSimpleMethods = new ClassWithSimpleMethods();
        ClassWithDelegateMethods classWithDelegateMethods = new ClassWithDelegateMethods();

        //The call is ambiguous between the following methods or properties: 
        //'test.ClassWithDelegateMethods.Method(System.Func<int,int>)' 
        //and 'test.ClassWithDelegateMethods.Method(test.ClassWithDelegateMethods.aDelegate)'
        classWithDelegateMethods.Method(classWithSimpleMethods.GetX);
    }
}

class ClassWithDelegateMethods
{
    public delegate string aDelegate(int x);
    public void Method(Func<int> func) { /* do something */ }
    public void Method(Func<string> func) { /* do something */ }
    public void Method(Func<int, int> func) { /* do something */ }
    public void Method(Func<string, string> func) { /* do something */ }
    public void Method(aDelegate ad) { }
}

class ClassWithSimpleMethods
{
    public string GetString() { return ""; }
    public int GetOne() { return 1; }
    public string GetX(int x) { return x.ToString(); }
} 

0

การบรรทุกเกินพิกัดFuncและActionคล้ายกัน (เพราะทั้งคู่เป็นผู้รับมอบอำนาจ)

string Function() // Func<string>
{
}

void Function() // Action
{
}

หากคุณสังเกตเห็นคอมไพลเลอร์จะไม่ทราบว่าจะเรียกตัวใดเพราะแตกต่างกันตามประเภทการส่งคืนเท่านั้น


ฉันไม่คิดว่ามันจะเป็นแบบนั้นจริงๆ - เพราะคุณไม่สามารถแปลง a Func<string>เป็นAction... และคุณไม่สามารถแปลงกลุ่มวิธีการที่ประกอบด้วยเมธอดที่ส่งคืนสตริงเป็นActionอย่างใดอย่างหนึ่ง
Jon Skeet

2
คุณไม่สามารถส่งตัวแทนที่ไม่มีพารามิเตอร์และกลับstringไปที่Actionไฟล์. ฉันไม่เห็นว่าทำไมถึงมีความคลุมเครือ
สัน

3
@dtb: ใช่การลบการโอเวอร์โหลดจะช่วยขจัดปัญหา - แต่นั่นไม่ได้อธิบายว่าทำไมถึงมีปัญหา
Jon Skeet
โดยการใช้ไซต์ของเรา หมายความว่าคุณได้อ่านและทำความเข้าใจนโยบายคุกกี้และนโยบายความเป็นส่วนตัวของเราแล้ว
Licensed under cc by-sa 3.0 with attribution required.