สร้างวัตถุใหม่หรือรีเซ็ตคุณสมบัติทั้งหมดหรือไม่


29
 public class MyClass
    {
        public object Prop1 { get; set; }

        public object Prop2 { get; set; }

        public object Prop3 { get; set; }
    }

สมมติว่าผมมีวัตถุmyObjectของMyClassและฉันต้องการที่จะตั้งค่าคุณสมบัติของมันก็จะดีกว่าการสร้างวัตถุใหม่หรือมอบหมายแต่ละคุณสมบัติ? สมมติว่าฉันไม่มีการใช้เพิ่มเติมใด ๆ กับอินสแตนซ์เก่า

myObject = new MyClass();

หรือ

myObject.Prop1 = null;
myObject.Prop2 = null;
myObject.Prop3 = null;


14
ไม่สามารถตอบได้โดยไม่มีบริบท
CodesInChaos

2
@CodesInChaos แน่นอน ตัวอย่างที่เฉพาะเจาะจงในการสร้างวัตถุใหม่อาจไม่เป็นที่ต้องการ: การรวบรวมวัตถุและพยายามลดการจัดสรรสำหรับการจัดการกับ GC ให้น้อยที่สุด
XNargaHuntress

@ XGundam05 แต่ถึงอย่างนั้นมันก็ไม่น่าเป็นไปได้มากที่เทคนิคนี้ที่แนะนำใน OP เป็นวิธีที่เหมาะสมในการจัดการกับมัน
Ben Aaronson

2
Suppose I have an object myObject of MyClass and I need to reset its properties- หากต้องการรีเซ็ตคุณสมบัติของวัตถุของคุณหรือถ้าเป็นประโยชน์ในการสร้างแบบจำลองโดเมนปัญหาทำไมไม่สร้างreset()วิธีการสำหรับ MyClass ดูคำตอบของ HarrisonPaine ด้านล่าง
Brandin

คำตอบ:


49

การสร้างอินสแตนซ์ของวัตถุใหม่นั้นดีกว่าเสมอจากนั้นคุณมีที่เดียวที่จะเริ่มต้นคุณสมบัติ (ตัวสร้าง) และสามารถอัปเดตได้อย่างง่ายดาย

ลองนึกภาพคุณเพิ่มคุณสมบัติใหม่ให้กับคลาสคุณควรจะปรับปรุงตัวสร้างมากกว่าการเพิ่มวิธีการใหม่ที่เริ่มต้นคุณสมบัติทั้งหมดอีกครั้ง

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


6
ตัวเลือกที่สามที่เป็นไปได้: myObject = new MyClass(oldObject);. แม้ว่านี่จะหมายถึงสำเนาที่ถูกต้องของวัตถุต้นฉบับในอินสแตนซ์ใหม่
AmazingDreams

ฉันคิดว่าnew MyClass(oldObject)นี่เป็นทางออกที่ดีที่สุดสำหรับกรณีที่การเริ่มต้นมีราคาแพง
rickcnagy

8
ตัวสร้างการคัดลอกเป็นลักษณะของ C ++ มากกว่า .NET ดูเหมือนว่าจะชอบวิธี Clone () ที่ส่งคืนอินสแตนซ์ใหม่ที่เป็นสำเนาที่แน่นอน
Eric

2
constuctor ไม่สามารถทำอะไรได้ง่าย ๆ แต่เรียกวิธีการเริ่มต้นสาธารณะ () เพื่อให้คุณยังคงมีจุดเริ่มต้นที่เดียวซึ่งสามารถเรียกจุดใดก็ได้เพื่อสร้างวัตถุใหม่ "ใหม่" อย่างมีประสิทธิภาพ ฉันใช้ไลบรารีที่มีบางอย่างเช่นอินเทอร์เฟซ IInitialize สำหรับวัตถุที่สามารถใช้ในกลุ่มวัตถุและนำมาใช้ใหม่เพื่อประสิทธิภาพ
ชาด Schouggins

16

แน่นอนคุณควรต้องการสร้างวัตถุใหม่ในขนาดใหญ่ส่วนใหญ่ของกรณี ปัญหาเกี่ยวกับการกำหนดคุณสมบัติทั้งหมดใหม่:

  • ต้องการตัวตั้งค่าสาธารณะสำหรับคุณสมบัติทั้งหมดซึ่ง จำกัด ระดับการห่อหุ้มอย่างมากที่คุณสามารถให้ได้
  • การรู้ว่าคุณมีการใช้งานเพิ่มเติมสำหรับอินสแตนซ์เก่าหมายความว่าคุณจำเป็นต้องรู้ทุกที่ว่ามีการใช้อินสแตนซ์เก่าหรือไม่ ดังนั้นหากคลาสAและคลาสBเป็นอินสแตนซ์ของคลาสที่ผ่านทั้งคู่Cพวกเขาต้องรู้ว่าพวกเขาจะถูกส่งผ่านอินสแตนซ์เดียวกันหรือไม่และถ้าเป็นเช่นนั้นอีกคลาสนั้นจะยังคงใช้งานอยู่หรือไม่ ชั้นเรียนสำหรับคู่รักที่แน่นหนาซึ่งไม่น่าจะมีเหตุผลอะไร
  • นำไปสู่การทำซ้ำรหัส - ตามที่ gbjbaanb ระบุไว้ถ้าคุณเพิ่มพารามิเตอร์ลงในตัวสร้างทุกที่ที่เรียกตัวสร้างจะไม่สามารถรวบรวมได้ไม่มีอันตรายจากการขาดจุด หากคุณเพิ่งเพิ่มคุณสมบัติสาธารณะคุณจะต้องตรวจสอบด้วยตนเองและอัปเดตทุกสถานที่ที่วัตถุ "รีเซ็ต"
  • เพิ่มความซับซ้อน ลองนึกภาพคุณกำลังสร้างและใช้อินสแตนซ์ของคลาสในลูป ถ้าคุณใช้วิธีการของคุณตอนนี้คุณต้องทำการเตรียมใช้งานแยกต่างหากในครั้งแรกที่ผ่านการวนรอบหรือก่อนเริ่มการวนรอบ ทั้งสองวิธีคือรหัสเพิ่มเติมที่คุณต้องเขียนเพื่อสนับสนุนวิธีการเริ่มต้นทั้งสองนี้
  • หมายความว่าชั้นเรียนของคุณไม่สามารถป้องกันตนเองจากการอยู่ในสถานะที่ไม่ถูกต้อง ลองนึกภาพคุณเขียนFractionชั้นเรียนที่มีตัวเศษและส่วนและต้องการบังคับให้มันลดลงเสมอ (เช่น gcd ของตัวเศษและตัวส่วนคือ 1) สิ่งนี้เป็นไปไม่ได้ที่จะทำอย่างดีถ้าคุณต้องการให้คนตั้งตัวเศษและส่วนที่เป็นสาธารณะเพราะพวกเขาอาจเปลี่ยนผ่านรัฐที่ไม่ถูกต้องเพื่อรับจากสถานะที่ถูกต้องหนึ่งไปยังอีก เช่น 1/2 (ใช้ได้) -> 2/2 (ไม่ถูกต้อง) -> 2/3 (ถูกต้อง)
  • ไม่ใช่เรื่องแปลกสำหรับภาษาที่คุณกำลังทำงานอยู่เพิ่มความเสียดทานทางปัญญาสำหรับทุกคนที่รักษารหัส

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

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


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

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


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

@Taemyr อาจเป็นไปได้ แต่ OP ระบุกฎอย่างชัดเจนในคำถาม
Ben Aaronson

9

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

MyObject.Reset(); // Sets all necessary properties to null

กว่า

MyObject = new MyClass();

ฉันจะไม่ต้องทำให้ผู้บริโภคในชั้นเรียนของคุณโทร

MyObject.Prop1 = null;
MyObject.Prop2 = null; // and so on

ถ้าคลาสแสดงถึงบางสิ่งที่สามารถรีเซ็ตได้มันควรเปิดเผยฟังก์ชันนั้นผ่านReset()วิธีการแทนที่จะอาศัยการเรียกตัวสร้างหรือการตั้งค่าคุณสมบัติด้วยตนเอง


5

ตามที่ Harrison Paine และ Brandin แนะนำฉันจะใช้วัตถุเดียวกันอีกครั้งและแยกตัวประกอบการกำหนดค่าเริ่มต้นของคุณสมบัติในวิธีการรีเซ็ต:

public class MyClass
{
    public MyClass() { this.Reset() }

    public void Reset() {
        this.Prop1 = whatever
        this.Prop2 = you name it
        this.Prop3 = oh yeah
    }

    public object Prop1 { get; set; }

    public object Prop2 { get; set; }

    public object Prop3 { get; set; }
}

สิ่งที่ฉันต้องการตอบ นี่คือสิ่งที่ดีที่สุดของทั้งสองโลก - และขึ้นอยู่กับกรณีการใช้งานตัวเลือกเดียวที่ใช้ได้ (Instance-Pooling)
Falco

2

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

ในบันทึกย่อที่เกี่ยวข้องมีรูปแบบไม่กี่รูปแบบที่สามารถใช้สำหรับวิธีการที่ต้องการส่งคืนข้อมูลในวัตถุ:

  1. วิธีการสร้างวัตถุใหม่ ส่งคืนการอ้างอิง

  2. วิธีการยอมรับการอ้างอิงไปยังวัตถุที่ไม่แน่นอนและเติมลงใน

  3. วิธีการยอมรับตัวแปรประเภทการอ้างอิงเป็นrefพารามิเตอร์และอาจใช้วัตถุที่มีอยู่หากเหมาะสมหรือเปลี่ยนตัวแปรเพื่อระบุวัตถุใหม่

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


1

ฉันคิดว่าคนส่วนใหญ่ตอบรับความนิยมในการสร้างวัตถุใหม่หายไปจากสถานการณ์ที่สำคัญ: การรวบรวมขยะ (GC) GC สามารถสร้างผลงานที่แท้จริงในแอปพลิเคชันที่สร้างวัตถุจำนวนมาก (คิดว่าเกมหรือแอปพลิเคชันทางวิทยาศาสตร์)

สมมุติว่าฉันมี tree expression ที่แสดงการแสดงออกทางคณิตศาสตร์โดยที่ nodes ด้านในนั้นเป็น nodes ฟังก์ชั่น (ADD, SUB, MUL, DIV) และ leaf nodes คือ terminal nodes (X, e, PI) หากคลาสโหนดของฉันมีเมธอด Evaluate () มันอาจจะเรียกใช้ลูก ๆ ซ้ำ ๆ และรวบรวมข้อมูลผ่านโหนดข้อมูล อย่างน้อยที่สุดโหนดใบไม้ทั้งหมดจะต้องสร้างวัตถุข้อมูลจากนั้นโหนดเหล่านั้นจะสามารถนำมาใช้อีกครั้งบนทางขึ้นต้นไม้จนกว่าจะมีการประเมินค่าสุดท้าย

ทีนี้สมมติว่าฉันมีต้นไม้นับพันต้นและฉันประเมินมันเป็นวง วัตถุข้อมูลเหล่านั้นทั้งหมดจะเรียกใช้ GC และทำให้ประสิทธิภาพการทำงานสูงขึ้น (การสูญเสียการใช้ประโยชน์ CPU สูงถึง 40% สำหรับการทำงานบางอย่างในแอปของฉัน - ฉันใช้ผู้สร้างโปรไฟล์เพื่อรับข้อมูล)

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

ฉันรู้สิ่งนี้เพราะฉันมีแอปพลิเคชันที่ได้ตีปัญหานี้และแก้ไขได้

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