แนวคิดการจับคู่สวิตช์ / รูปแบบ


151

ฉันได้ดู F # เมื่อเร็ว ๆ นี้และในขณะที่ฉันไม่ได้กระโดดข้ามรั้วในไม่ช้ามันก็ไฮไลท์บางพื้นที่ที่ C # (หรือการสนับสนุนห้องสมุด) จะทำให้ชีวิตง่ายขึ้น

โดยเฉพาะอย่างยิ่งฉันกำลังคิดเกี่ยวกับความสามารถในการจับคู่รูปแบบของ F # ซึ่งอนุญาตให้ใช้ไวยากรณ์ที่หลากหลายมากซึ่งแสดงออกได้ดีกว่าสวิตช์ C / เงื่อนไขที่เทียบเท่าในปัจจุบัน ฉันจะไม่พยายามยกตัวอย่างโดยตรง (F # ของฉันไม่ได้ขึ้นอยู่กับมัน) แต่ในระยะสั้นจะช่วยให้:

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

ในขณะที่มันน่ารักสำหรับ C # ในที่สุดก็ยืม [ahem] ความร่ำรวยบางอย่างในระหว่างนี้ฉันได้ดูสิ่งที่สามารถทำได้ในรันไทม์ - ตัวอย่างเช่นมันค่อนข้างง่ายที่จะรวมวัตถุบางอย่างเข้าด้วยกัน:

var getRentPrice = new Switch<Vehicle, int>()
        .Case<Motorcycle>(bike => 100 + bike.Cylinders * 10) // "bike" here is typed as Motorcycle
        .Case<Bicycle>(30) // returns a constant
        .Case<Car>(car => car.EngineType == EngineType.Diesel, car => 220 + car.Doors * 20)
        .Case<Car>(car => car.EngineType == EngineType.Gasoline, car => 200 + car.Doors * 20)
        .ElseThrow(); // or could use a Default(...) terminator

โดยที่ getRentPrice คือ Func <Vehicle, int>

[หมายเหตุ - อาจจะเปลี่ยน / กรณีที่นี่เป็นคำที่ผิด ... แต่มันแสดงให้เห็นความคิด]

สำหรับฉันมันชัดเจนกว่าการใช้ซ้ำถ้า / / หรือคอมโพสิตเงื่อนไขที่ประกอบไปด้วยสาม (ซึ่งได้รับยุ่งมากสำหรับการแสดงออกที่ไม่น่าสนใจ - วงเล็บมากมาย) นอกจากนี้ยังหลีกเลี่ยงจำนวนมากของการหล่อและช่วยให้การขยายง่าย (ทั้งโดยตรงหรือผ่านทางวิธีการขยาย) เพื่อการแข่งขันมากขึ้นเฉพาะสำหรับตัวอย่าง inrange ( ... ) ตรงกับเปรียบได้กับ VB เลือก ... กรณี "X เป็น Y "การใช้งาน

ฉันแค่พยายามที่จะวัดว่าผู้คนคิดว่ามีประโยชน์มากจากการสร้างดังกล่าวข้างต้น (ในกรณีที่ไม่มีการสนับสนุนภาษา)

โปรดทราบว่าฉันได้เล่นกับ 3 ตัวแปรข้างต้น:

  • เวอร์ชัน Func <TSource, TValue> สำหรับการประเมิน - เปรียบเทียบได้กับคำสั่งเงื่อนไขประกอบไปด้วยสามส่วน
  • เวอร์ชัน Action <TSource> เปรียบเทียบได้กับ if / else if / else if / else if / else
  • Expression <Func <TSource, TValue >> version - เป็นรุ่นแรก แต่ใช้ได้โดยผู้ให้บริการ LINQ ตามอำเภอใจ

นอกจากนี้การใช้เวอร์ชันที่อิงกับ Expression จะช่วยให้สามารถเขียนใหม่ Expression-tree ได้โดยการฝังสาขาทั้งหมดลงใน Expression แบบมีเงื่อนไขแบบคอมโพสิตแทนที่จะใช้การเรียกซ้ำ ฉันไม่ได้ตรวจสอบเมื่อเร็ว ๆ นี้ แต่ใน Entity Framework รุ่นก่อน ๆ บางรุ่นนั้นดูเหมือนว่าฉันจำได้ว่ามันจำเป็นเพราะไม่ชอบ Invocation Expression มาก นอกจากนี้ยังช่วยให้การใช้งานที่มีประสิทธิภาพยิ่งขึ้นด้วย LINQ-to-Objects เนื่องจากจะหลีกเลี่ยงการมอบหมายตัวแทนซ้ำ - การทดสอบแสดงการจับคู่เหมือนด้านบน (โดยใช้แบบฟอร์ม Expression) ดำเนินการที่ความเร็วเดียวกัน คำสั่งเงื่อนไขแบบรวม เพื่อความสมบูรณ์ Func <... > เวอร์ชั่นที่ใช้นั้นใช้เวลานานกว่าคำสั่งเงื่อนไข C # ถึง 4 เท่า แต่ยังเร็วมากและไม่น่าจะเป็นปัญหาคอขวดที่สำคัญในกรณีใช้งานส่วนใหญ่

ฉันยินดีต้อนรับความคิดใด ๆ / การป้อนข้อมูล / การวิจารณ์ / ฯลฯ ที่ด้านบน (หรือในความเป็นไปได้ของการสนับสนุนภาษา C # ยิ่งขึ้น ... นี่คือความหวัง ;-p)


"ฉันแค่พยายามที่จะวัดว่าคนคิดว่ามีประโยชน์มากจากการสร้างเช่นข้างต้น (ในกรณีที่ไม่มีการสนับสนุนภาษา)?" IMHO ใช่ ไม่มีสิ่งที่คล้ายกันอยู่แล้วหรือ ถ้าไม่รู้สึกสนับสนุนให้เขียนห้องสมุดที่มีน้ำหนักเบา
Konrad Rudolph

10
คุณสามารถใช้ VB .NET ซึ่งสนับสนุนสิ่งนี้ในคำสั่งกรณีเลือก จี๊ด!
จิมเบอร์เกอร์

ฉันจะตุ๊ดฮอร์นของตัวเองและเพิ่มลิงค์ไปยังห้องสมุดของฉัน: functional-dotnet
Alexey Romanov

1
ฉันชอบความคิดนี้และทำให้รูปแบบสวิตช์และตัวเรือนมีความสวยงามและยืดหยุ่นมากขึ้น แต่นี่ไม่ใช่วิธีการใช้งานที่เหมือนจริงของการใช้ไวยากรณ์เหมือน Linq เป็น wrapper if-then? ฉันจะกีดกันใครบางคนจากการใช้สิ่งนี้แทนข้อตกลงจริงเช่นswitch-caseคำสั่ง อย่าเข้าใจฉันผิดฉันคิดว่ามันมีสถานที่และฉันอาจจะมองหาวิธีที่จะใช้
IAbstract

2
แม้ว่าคำถามนี้จะมีอายุมากกว่าสองปี แต่ก็รู้สึกว่าเกี่ยวข้องกับการพูดถึงว่า C # 7 กำลังจะเปิดตัวในไม่ช้า (ish) ที่มีความสามารถในการจับคู่รูปแบบ
Abion47

คำตอบ:


22

ฉันรู้ว่ามันเป็นหัวข้อเก่า แต่ใน c # 7 คุณสามารถทำได้:

switch(shape)
{
    case Circle c:
        WriteLine($"circle with radius {c.Radius}");
        break;
    case Rectangle s when (s.Length == s.Height):
        WriteLine($"{s.Length} x {s.Height} square");
        break;
    case Rectangle r:
        WriteLine($"{r.Length} x {r.Height} rectangle");
        break;
    default:
        WriteLine("<unknown shape>");
        break;
    case null:
        throw new ArgumentNullException(nameof(shape));
}

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

37

หลังจากพยายามทำสิ่งที่ "ใช้งานได้" ใน C # (และพยายามทำหนังสือด้วย) ฉันก็ได้ข้อสรุปว่าไม่มีข้อยกเว้นบางประการสิ่งต่าง ๆ ไม่ได้ช่วยอะไรมากนัก

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

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

"ปัญหา" คือเมื่อคุณเริ่มใช้แนวคิดการทำงานบางอย่างเป็นเรื่องปกติที่จะต้องการดำเนินการต่อ อย่างไรก็ตามการใช้ประโยชน์จากสิ่งอันดับฟังก์ชั่นการประยุกต์ใช้บางส่วนวิธีการและ currying, จับคู่รูปแบบฟังก์ชั่นที่ซ้อนกัน generics สนับสนุน monad ฯลฯ ใน C # ได้รับมากน่าเกลียดมากอย่างรวดเร็ว มันสนุกและคนฉลาด ๆ บางคนได้ทำสิ่งดีๆใน C # แต่จริงๆแล้วการใช้มันให้ความรู้สึกหนัก

สิ่งที่ฉันลงเอยด้วยการใช้บ่อย (ข้ามโครงการ) ใน C #:

  • ฟังก์ชั่นลำดับผ่านวิธีการขยายสำหรับ IEnumerable สิ่งที่ต้องการ ForEach หรือกระบวนการ ("ใช้" หรือไม่ - ดำเนินการกับรายการลำดับตามที่ระบุ) เหมาะสมเนื่องจากไวยากรณ์ C # รองรับได้ดี
  • สรุปรูปแบบคำสั่งทั่วไป การลอง / จับ / สุดท้ายบล็อกที่ซับซ้อนหรือบล็อกโค้ดอื่นที่เกี่ยวข้อง การขยาย LINQ-to-SQL ก็เหมาะสมเช่นกัน
  • Tuples ในระดับหนึ่ง

** แต่อย่าลืม: การขาดการวางนัยแบบอัตโนมัติและการอนุมานประเภทเป็นอุปสรรคต่อการใช้คุณสมบัติเหล่านี้ **

ทั้งหมดนี้พูดตามที่คนอื่นพูดถึงในทีมเล็ก ๆ เพื่อจุดประสงค์เฉพาะใช่บางทีพวกเขาอาจช่วยได้หากคุณติดอยู่กับ C # แต่จากประสบการณ์ของฉันพวกเขามักจะรู้สึกยุ่งยากมากกว่าพวกเขามีค่า - YMMV

ลิงก์อื่น ๆ :


25

เหตุผลที่ C # ไม่ได้ทำให้ง่ายต่อการสลับกับชนิดเพราะส่วนใหญ่เป็นภาษาเชิงวัตถุและวิธีการ 'ถูกต้อง' ในการทำสิ่งนี้ในแง่ของวัตถุจะกำหนดวิธี GetRentPrice บนยานพาหนะและ แทนที่ในคลาสที่ได้รับ

ที่กล่าวว่าฉันใช้เวลาเล่นกับหลายกระบวนทัศน์และภาษาที่ใช้งานได้เช่น F # และ Haskell ซึ่งมีความสามารถประเภทนี้และฉันได้พบสถานที่ต่าง ๆ ที่มันจะมีประโยชน์มาก่อน (เช่นเมื่อคุณ ไม่ได้เขียนประเภทที่คุณต้องเปิดเพื่อให้คุณไม่สามารถใช้วิธีเสมือนกับพวกเขา) และมันเป็นสิ่งที่ฉันยินดีต้อนรับสู่ภาษาพร้อมกับสหภาพที่แบ่งแยก

[แก้ไข: นำส่วนที่เกี่ยวกับประสิทธิภาพออกตามที่ Marc ระบุว่าอาจทำให้เกิดการลัดวงจร]

ปัญหาที่อาจเกิดขึ้นอีกข้อหนึ่งคือการใช้งาน - ชัดเจนจากการโทรครั้งสุดท้ายจะเกิดอะไรขึ้นถ้าการแข่งขันไม่สามารถปฏิบัติตามเงื่อนไขใด ๆ ได้ แต่จะมีพฤติกรรมอย่างไรหากตรงกับเงื่อนไขสองข้อขึ้นไป? มันควรจะเป็นข้อยกเว้น? มันควรจะกลับมาเป็นนัดแรกหรือนัดสุดท้ายหรือไม่

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


1
ที่จริง - เวอร์ชันที่ฉันทำมีการลัดวงจรทั้งในเวอร์ชันตัวแทนและเวอร์ชัน เวอร์ชันนิพจน์จะคอมไพล์กับเงื่อนไขแบบผสม เวอร์ชันผู้แทนเป็นเพียงชุดของเพรดิเคตและ func / แอ็คชั่น - เมื่อการแข่งขันหยุดลง
Marc Gravell

น่าสนใจ - จากรูปลักษณ์คร่าวๆฉันคิดว่ามันจะต้องทำการตรวจสอบขั้นพื้นฐานอย่างน้อยในแต่ละเงื่อนไขเพราะดูเหมือนว่าเป็นห่วงโซ่วิธีการ แต่ตอนนี้ฉันรู้แล้วว่าวิธีการต่างๆ ฉันจะแก้ไขคำตอบเพื่อลบคำสั่งนั้น
Greg Beech

22

ฉันไม่คิดว่าห้องสมุดประเภทนี้ (ซึ่งทำหน้าที่คล้ายกับส่วนขยายภาษา) มีแนวโน้มที่จะได้รับการยอมรับอย่างกว้างขวาง แต่พวกเขาก็สนุกกับการเล่นและจะมีประโยชน์มากสำหรับทีมขนาดเล็กที่ทำงานในโดเมนเฉพาะที่มีประโยชน์ ตัวอย่างเช่นถ้าคุณกำลังเขียน 'กฎเกณฑ์ทางธุรกิจ / ตรรกะ' ที่ทำแบบทดสอบตามอำเภอใจเช่นนี้และอะไรก็ตามฉันก็จะเห็นว่ามันจะมีประโยชน์อย่างไร

ฉันไม่สงสัยเลยว่านี่น่าจะเป็นคุณลักษณะภาษา C # (ดูเหมือนจะสงสัย แต่ใครจะได้เห็นอนาคต)

สำหรับการอ้างอิง F # ที่เกี่ยวข้องมีค่าโดยประมาณ:

let getRentPrice (v : Vehicle) = 
    match v with
    | :? Motorcycle as bike -> 100 + bike.Cylinders * 10
    | :? Bicycle -> 30
    | :? Car as car when car.EngineType = Diesel -> 220 + car.Doors * 20
    | :? Car as car when car.EngineType = Gasoline -> 200 + car.Doors * 20
    | _ -> failwith "blah"

สมมติว่าคุณกำหนดลำดับชั้นของคลาสตามบรรทัดของ

type Vehicle() = class end

type Motorcycle(cyl : int) = 
    inherit Vehicle()
    member this.Cylinders = cyl

type Bicycle() = inherit Vehicle()

type EngineType = Diesel | Gasoline

type Car(engType : EngineType, doors : int) = 
    inherit Vehicle()
    member this.EngineType = engType
    member this.Doors = doors

2
ขอบคุณสำหรับเวอร์ชั่น F # ฉันเดาว่าฉันชอบวิธีที่ F # จัดการกับสิ่งนี้ แต่ฉันไม่แน่ใจว่า (โดยรวม) F # เป็นตัวเลือกที่ถูกต้องในขณะนี้ดังนั้นฉันต้องเดินไปที่พื้นตรงกลาง ...
Marc Gravell

13

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

นี่คือการใช้งานคลาสที่ให้ (เกือบ) ไวยากรณ์เดียวกับที่คุณอธิบาย

public class PatternMatcher<Output>
{
    List<Tuple<Predicate<Object>, Func<Object, Output>>> cases = new List<Tuple<Predicate<object>,Func<object,Output>>>();

    public PatternMatcher() { }        

    public PatternMatcher<Output> Case(Predicate<Object> condition, Func<Object, Output> function)
    {
        cases.Add(new Tuple<Predicate<Object>, Func<Object, Output>>(condition, function));
        return this;
    }

    public PatternMatcher<Output> Case<T>(Predicate<T> condition, Func<T, Output> function)
    {
        return Case(
            o => o is T && condition((T)o), 
            o => function((T)o));
    }

    public PatternMatcher<Output> Case<T>(Func<T, Output> function)
    {
        return Case(
            o => o is T, 
            o => function((T)o));
    }

    public PatternMatcher<Output> Case<T>(Predicate<T> condition, Output o)
    {
        return Case(condition, x => o);
    }

    public PatternMatcher<Output> Case<T>(Output o)
    {
        return Case<T>(x => o);
    }

    public PatternMatcher<Output> Default(Func<Object, Output> function)
    {
        return Case(o => true, function);
    }

    public PatternMatcher<Output> Default(Output o)
    {
        return Default(x => o);
    }

    public Output Match(Object o)
    {
        foreach (var tuple in cases)
            if (tuple.Item1(o))
                return tuple.Item2(o);
        throw new Exception("Failed to match");
    }
}

นี่คือรหัสทดสอบบางส่วน:

    public enum EngineType
    {
        Diesel,
        Gasoline
    }

    public class Bicycle
    {
        public int Cylinders;
    }

    public class Car
    {
        public EngineType EngineType;
        public int Doors;
    }

    public class MotorCycle
    {
        public int Cylinders;
    }

    public void Run()
    {
        var getRentPrice = new PatternMatcher<int>()
            .Case<MotorCycle>(bike => 100 + bike.Cylinders * 10) 
            .Case<Bicycle>(30) 
            .Case<Car>(car => car.EngineType == EngineType.Diesel, car => 220 + car.Doors * 20)
            .Case<Car>(car => car.EngineType == EngineType.Gasoline, car => 200 + car.Doors * 20)
            .Default(0);

        var vehicles = new object[] {
            new Car { EngineType = EngineType.Diesel, Doors = 2 },
            new Car { EngineType = EngineType.Diesel, Doors = 4 },
            new Car { EngineType = EngineType.Gasoline, Doors = 3 },
            new Car { EngineType = EngineType.Gasoline, Doors = 5 },
            new Bicycle(),
            new MotorCycle { Cylinders = 2 },
            new MotorCycle { Cylinders = 3 },
        };

        foreach (var v in vehicles)
        {
            Console.WriteLine("Vehicle of type {0} costs {1} to rent", v.GetType(), getRentPrice.Match(v));
        }
    }

9

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

มีข้อผิดพลาดในการออกแบบภาษาแบบหลายกระบวนทัศน์ในทางกลับกันมันดีมากที่มี lambdas ใน C # และ Haskell สามารถทำสิ่งที่จำเป็นเช่น IO แต่มันไม่ได้เป็นวิธีที่สง่างามมากไม่ใช่ในรูปแบบของ Haskell

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

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


1
อาจจะ. จริง ๆ แล้วฉันจะพยายามคิดหาเหตุผล "ฆาตกร" ที่น่าเชื่อถือว่าทำไมมันถึงเป็นสิ่งจำเป็น (ซึ่งตรงข้ามกับ "อาจจะดีในบางกรณีที่ค่าใช้จ่ายในการทำให้ภาษามีความซับซ้อนมากขึ้น")
Marc Gravell

5

IMHO วิธี OO ในการทำสิ่งต่าง ๆ ดังกล่าวเป็นรูปแบบของผู้เข้าชม วิธีการสมาชิกผู้มาเยี่ยมชมของคุณทำหน้าที่เป็นกรณีสร้างและคุณปล่อยให้ภาษาจัดการการแจกจ่ายที่เหมาะสมโดยไม่ต้อง "มอง" ที่ประเภท


4

แม้ว่ามันจะไม่ใช่ 'C-sharpey' ที่จะเปิดชนิด แต่ฉันรู้ว่าโครงสร้างจะมีประโยชน์ในการใช้งานทั่วไป - ฉันมีโครงการส่วนบุคคลอย่างน้อยหนึ่งโครงการที่สามารถใช้งานได้ (แม้ว่า ATM ที่จัดการได้) มีปัญหาด้านประสิทธิภาพการคอมไพล์หรือไม่ด้วยนิพจน์ทรีที่เขียนใหม่?


ไม่ใช่ถ้าคุณแคชวัตถุเพื่อนำกลับมาใช้ใหม่ (ซึ่งส่วนใหญ่เป็นการทำงานของนิพจน์ C # lambda ยกเว้นคอมไพเลอร์ซ่อนรหัส) การเขียนซ้ำช่วยปรับปรุงประสิทธิภาพการรวบรวมอย่างแน่นอน - อย่างไรก็ตามสำหรับการใช้งานปกติ (แทนที่จะเป็น LINQ-to-Something) ฉันคาดว่ารุ่นตัวแทนอาจมีประโยชน์มากกว่า
Marc Gravell

หมายเหตุด้วย - มันไม่จำเป็นต้องเป็นสวิตช์บนชนิด - มันสามารถใช้เป็นคอมโพสิตแบบมีเงื่อนไข (แม้กระทั่งถึง LINQ) - แต่ไม่มียุ่ง x => ทดสอบ? ผลที่ 1: (ทดสอบ 2 ผลที่ 2: (ทดสอบ 3 ผล 3: ผลลัพธ์ 4))
Marc Gravell

ดีใจที่ได้ทราบแม้ว่าฉันหมายถึงประสิทธิภาพของการรวบรวมจริง: csc.exe ใช้เวลานานเท่าใดฉันไม่คุ้นเคยกับ C # มากพอที่จะรู้ว่ามันเป็นปัญหาจริงๆหรือไม่ แต่เป็นปัญหาใหญ่สำหรับ C ++
Simon Buchan

CSC จะไม่กระพริบตานี้ - มันจึงคล้ายกับวิธีการทำงานของ LINQ และ C # 3.0 คอมไพเลอร์ค่อนข้างดีที่วิธีการ LINQ / ขยาย ฯลฯ
Marc Gravell

3

ฉันคิดว่านี่น่าสนใจจริงๆ (+1) แต่สิ่งหนึ่งที่ต้องระวัง: คอมไพเลอร์ C # ค่อนข้างดีในการเพิ่มประสิทธิภาพของคำสั่งสวิตช์ ไม่เพียง แต่สำหรับการลัดวงจรเท่านั้นคุณจะได้รับ IL ที่แตกต่างกันโดยสิ้นเชิงโดยขึ้นอยู่กับว่ามีกี่กรณี

ตัวอย่างเฉพาะของคุณทำสิ่งที่ฉันพบว่ามีประโยชน์มาก - ไม่มีรูปแบบที่เทียบเท่ากับกรณีตามประเภทเนื่องจาก (ตัวอย่าง) typeof(Motorcycle)ไม่ใช่ค่าคงที่

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


0

คุณสามารถบรรลุสิ่งที่คุณเป็นหลังจากใช้ห้องสมุดที่ฉันเขียนเรียกว่าOneOf

ข้อได้เปรียบที่สำคัญเหนือกว่าswitch(และifและexceptions as control flow) คือมันปลอดภัยในการรวบรวมเวลา - ไม่มีตัวจัดการเริ่มต้นหรือล้มเหลว

   OneOf<Motorcycle, Bicycle, Car> vehicle = ... //assign from one of those types
   var getRentPrice = vehicle
        .Match(
            bike => 100 + bike.Cylinders * 10, // "bike" here is typed as Motorcycle
            bike => 30, // returns a constant
            car => car.EngineType.Match(
                diesel => 220 + car.Doors * 20
                petrol => 200 + car.Doors * 20
            )
        );

อยู่ใน Nuget และเป้าหมาย net451 และ netstandard1.6

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