ข้อดีของการใช้เมธอดแบบสแตติกส่วนตัว


209

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

ตัวอย่าง:

foreach (XmlElement element in xmlDoc.DocumentElement.SelectNodes("sample"))
{
    string first = GetInnerXml(element, ".//first");
    string second = GetInnerXml(element, ".//second");
    string third = GetInnerXml(element, ".//third");
}

...

private static string GetInnerXml(XmlElement element, string nodeName)
{
    return GetInnerXml(element, nodeName, null);
}

private static string GetInnerXml(XmlElement element, string nodeName, string defaultValue)
{
    XmlNode node = element.SelectSingleNode(nodeName);
    return node == null ? defaultValue : node.InnerXml;
}

มีประโยชน์ใด ๆ ในการประกาศวิธี GetInnerXml () เป็นแบบคงที่หรือไม่ ไม่มีความเห็นโปรดตอบฉันมีความคิดเห็น


1
ความเป็นไปได้ที่ซ้ำกันของวิธีการสามารถทำให้คงที่ แต่ควร?
drzaus

คำตอบ:


221

จากหน้ากฎ FxCopในสิ่งนี้:

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


37
ฉันยังจะเพิ่มว่าประโยค "คงที่" ไม่เป็นอันตรายและให้ "เอกสาร" บางส่วนกับ 1 คำแล้ว มันจะบอกคุณว่าวิธีนี้ไม่ได้ใช้อินสแตนซ์สมาชิกใด ๆ และคุณจะได้รับเอกสารฉบับนี้เกือบฟรี
frandevel

20
ฉันจะไปพูดว่า: "หากวิธีการไม่ต้องการการเข้าถึงของรัฐ (นี้) ทำให้คงที่" เป็นกฎทั่วไป
DanMan

3
เพื่อประโยชน์ของความสมดุลมันคุ้มค่าที่ชี้ให้เห็นว่าผู้คนจำนวนมากมักจะต่อต้านวิธีการคงที่เพราะพวกเขาทำลายความหลากหลายและหมายความว่าวัตถุไม่สามารถถูก stubbed สำหรับการทดสอบ ตัวอย่างเช่นgoogletesting.blogspot.co.uk/2008/12/…
Andy

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

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

93

เมื่อฉันเขียนชั้นเรียนวิธีการส่วนใหญ่แบ่งออกเป็นสองประเภท:

  • วิธีการที่ใช้ / เปลี่ยนสถานะของอินสแตนซ์ปัจจุบัน
  • วิธีใช้ตัวช่วยที่ไม่ได้ใช้ / เปลี่ยนสถานะของวัตถุปัจจุบัน แต่ช่วยฉันคำนวณค่าที่ฉันต้องการที่อื่น

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

ใช้ตัวอย่างนี้:

ห้องสมุดสาธารณะ
{
    หนังสือส่วนตัวแบบคงที่ findBook (รายการ <Book> หนังสือ, ชื่อสตริง)
    {
        // code ไปที่นี่
    }
}

หากสถานะของห้องสมุดผิดพลาดและฉันพยายามหาสาเหตุว่าทำไมฉันจึงสามารถแยก FindBook ออกมาเป็นผู้ร้ายได้

ฉันพยายามสื่อสารให้มากที่สุดเท่าที่จะทำได้ด้วยวิธีการหรือลายเซ็นของฟังก์ชั่นและนี่เป็นวิธีที่ยอดเยี่ยมในการทำเช่นนั้น


1
การประกาศเมธอดแบบ const ใน C ++ ใช่ไหม
anhoppe

ใช่ - เป็นอีกวิธีที่ดีในการใช้ภาษาเพื่อทำให้สิ่งต่าง ๆ ง่ายขึ้นโดย จำกัด สิ่งที่อาจผิดพลาด
Neil

สิ่งนี้ไม่เป็นความจริง สมมติว่า a Libraryมีเขตข้อมูลอินสแตนซ์List<Book> _booksสำหรับเก็บหนังสือของคุณ (ไม่ใช่วิธีที่คุณออกแบบLibraryชั้นเรียน แต่อาจมี w / e) และส่งผ่านรายการนี้ไปยังfindBookและวิธีแบบคงที่จะเรียกใช้books.Clear()หรือbooks.Reverse()อื่น ๆ ถ้าคุณให้การเข้าถึงวิธีการแบบคงที่เพื่ออ้างอิงถึงบางสถานะที่ไม่แน่นอนดังนั้นวิธีการแบบคงที่สามารถทำให้สถานะของคุณยุ่งเหยิงได้เป็นอย่างดี
ร่า

1
จริง และในกรณีนั้นลายเซ็นจะแสดงให้เราเห็นว่าวิธีนี้มีการเข้าถึง (และความสามารถในการกลายพันธุ์) ตัวอย่างของ Library
Neil

สำหรับโครงสร้างป้องกันที่เราอาจใช้จริง ๆ มีวิธีที่จะบ่อนทำลายมัน แต่การใช้พวกมันยังฉลาดและช่วยผลักดันเราไปในทิศทางที่ถูกต้องสู่ "หลุมแห่งความสำเร็จ"
Neil

81

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

src: MSDN - http://msdn.microsoft.com/en-us/library/79b3xss3(v=vs.110).aspx


15

ใช่คอมไพเลอร์ไม่จำเป็นต้องผ่านthisตัวชี้โดยนัยไปยังstaticเมธอด แม้ว่าคุณจะไม่ได้ใช้มันในวิธีการอินสแตนซ์ของคุณก็ยังคงถูกส่งผ่าน


สิ่งนี้เกี่ยวข้องกับประสิทธิภาพหรือความได้เปรียบของหน่วยความจำในขณะใช้งานจริงอย่างไร
Scott Dorman

11
การส่งพารามิเตอร์พิเศษหมายความว่า CPU ต้องทำงานพิเศษเพื่อวางพารามิเตอร์นั้นในรีจิสเตอร์และผลักมันลงบนสแต็กหากเมธอดอินสแตนซ์เรียกใช้วิธีอื่น
Kent Boogaart

5

มันจะเร็วขึ้นเล็กน้อยเนื่องจากไม่มีการส่งพารามิเตอร์นี้ (แม้ว่าค่าใช้จ่ายในการเรียกใช้วิธีการอาจจะมากกว่าการบันทึกนี้)

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


4

สิ่งนี้บังคับให้คุณจำไว้ว่าให้ประกาศสมาชิกที่มีคลาสที่ฟังก์ชันใช้เป็นแบบคงที่เช่นกันซึ่งควรบันทึกหน่วยความจำของการสร้างรายการเหล่านั้นสำหรับแต่ละอินสแตนซ์


เพียงเพราะมันเป็นตัวแปรระดับชั้นไม่ได้หมายความว่ามันควรจะคงที่
Scott Dorman

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

2

ฉันชอบวิธีการส่วนตัวทุกอย่างมากที่จะคงที่เว้นแต่พวกเขาไม่สามารถ ฉันชอบสิ่งต่อไปนี้มาก:

public class MyClass
{
    private readonly MyDependency _dependency;

    public MyClass(MyDependency dependency)
    {
        _dependency = dependency;
    }

    public int CalculateHardStuff()
    {
        var intermediate = StepOne(_dependency);
        return StepTwo(intermediate);
    }

    private static int StepOne(MyDependency dependency)
    {
        return dependency.GetFirst3Primes().Sum();
    }

    private static int StepTwo(int intermediate)
    {
        return (intermediate + 5)/4;
    }
}

public class MyDependency
{
    public IEnumerable<int> GetFirst3Primes()
    {
        yield return 2;
        yield return 3;
        yield return 5;
    }
}

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

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

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


-3

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


4
มันไม่สมเหตุสมผลเลย ฮีปจะไม่เก็บหน่วยความจำสำหรับรหัสสำหรับวิธีการใด ๆคงที่หรืออย่างอื่น ฮีปมีไว้สำหรับอินสแตนซ์ของวัตถุ จะมีข้อมูลในสแต็กสำหรับการเรียกใช้เมธอดใด ๆ (เพื่อเก็บหน่วยความจำสำหรับพารามิเตอร์, ค่าส่งคืน, โลคอลที่ไม่ใช่แบบยก ฯลฯ ) แต่ทั้งหมดจะหายไปเมื่อวิธีดำเนินการเสร็จสิ้น
Servy
โดยการใช้ไซต์ของเรา หมายความว่าคุณได้อ่านและทำความเข้าใจนโยบายคุกกี้และนโยบายความเป็นส่วนตัวของเราแล้ว
Licensed under cc by-sa 3.0 with attribution required.