การทำให้เป็นจริงคืออะไร?


163

ฉันรู้ว่า Java ใช้ตัวแปรหลากหลาย (Generics) กับการลบ ฉันเข้าใจว่าการลบคืออะไร

ฉันรู้ว่า C # ใช้ตัวแปรความแปรปรวนร่วมกับการทำให้เป็นจริง ฉันรู้ว่าสามารถทำให้คุณเขียน

public void dosomething(List<String> input) {}
public void dosomething(List<Int> input) {}

หรือว่าคุณสามารถรู้ตอนรันไทม์พารามิเตอร์ประเภทของบางประเภทพารามิเตอร์คืออะไร แต่ฉันไม่เข้าใจว่ามันคืออะไร

  • ประเภท reified คืออะไร?
  • ค่า reified คืออะไร?
  • จะเกิดอะไรขึ้นเมื่อมีการแก้ไขประเภท / ค่า

มันไม่ได้เป็นคำตอบ แต่อาจช่วยsomeway
heringer

@ ผู้นำที่ดูเหมือนจะตอบคำถาม "อะไรคือการลบ" ค่อนข้างดีและดูเหมือนว่าจะตอบโดยทั่วไป "สิ่งที่ reification" กับ "ไม่ลบ" - ชุดรูปแบบทั่วไปที่ฉันพบเมื่อเริ่มค้นหาคำตอบก่อนที่จะโพสต์ที่นี่
Martijn

5
... และมีฉันคิด re ification เป็นกระบวนการของการแปลงswitchกลับไปเป็นif/ elseเมื่อก่อนหน้านี้ได้ถูกแปลงจากif/ elseเป็นswitch...
Digital Trauma

8
Res , reisเป็นภาษาละตินสำหรับสิ่งเพื่อทำให้เป็นจริงเป็นอักษรthingification ฉันไม่มีประโยชน์อะไรที่จะมีส่วนช่วยเท่าที่ C # ใช้คำศัพท์ แต่ความจริงที่ตัวเองใช้มันทำให้ฉันยิ้มได้
KRyan

คำตอบ:


209

Reification เป็นกระบวนการของการทำสิ่งที่เป็นนามธรรมและสร้างสิ่งที่เป็นรูปธรรม

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

เพื่อวลีมันแตกต่างกันมันเป็นกระบวนการของการใช้คำจำกัดความList<T>และintและผลิตList<int>ประเภทที่เป็นรูปธรรม

หากต้องการทำความเข้าใจเพิ่มเติมให้เปรียบเทียบแนวทางต่อไปนี้:

  • ใน Java generics นิยามประเภททั่วไปจะถูกแปลงเป็นประเภททั่วไปหนึ่งคอนกรีตที่ใช้ร่วมกันในการรวมอาร์กิวเมนต์ประเภทที่ได้รับอนุญาตทั้งหมด ดังนั้นหลาย ๆ (ระดับรหัสแหล่งที่มา) ประเภทถูกแมปไป (ระดับไบนารี) ประเภทหนึ่ง - แต่เป็นผลให้ข้อมูลเกี่ยวกับการขัดแย้งประเภทของตัวอย่างจะถูกยกเลิกในอินสแตนซ์ (ชนิดลบออก)

    1. ในฐานะที่เป็นผลข้างเคียงของเทคนิคการใช้งานนี้ข้อโต้แย้งประเภททั่วไปเท่านั้นที่ได้รับอนุญาตโดยกำเนิดคือประเภทเหล่านั้นที่สามารถแบ่งปันรหัสไบนารี่ของประเภทที่เป็นรูปธรรมของพวกเขา; ซึ่งหมายความว่าประเภทที่มีที่เก็บข้อมูลมีการแทนกันได้; ซึ่งหมายถึงประเภทอ้างอิง การใช้ประเภทค่าเป็นอาร์กิวเมนต์ประเภททั่วไปต้องใช้มวย (วางไว้ใน wrapper ประเภทการอ้างอิงอย่างง่าย)
    2. ไม่มีการทำซ้ำรหัสเพื่อใช้งาน Generics ด้วยวิธีนี้
    3. ข้อมูลประเภทที่อาจมีอยู่ในขณะใช้งาน (ใช้การสะท้อนภาพ) จะหายไป ในทางกลับกันนี่หมายความว่าความเชี่ยวชาญเฉพาะประเภททั่วไป (ความสามารถในการใช้ซอร์สโค้ดพิเศษสำหรับการรวมอาร์กิวเมนต์ทั่วไปโดยเฉพาะ) นั้น จำกัด มาก
    4. กลไกนี้ไม่ต้องการการสนับสนุนจากสภาพแวดล้อมรันไทม์
    5. มีวิธีแก้ไขปัญหาเล็กน้อยเพื่อเก็บข้อมูลประเภทที่โปรแกรม Java หรือภาษาที่ใช้ JVM สามารถใช้ได้
  • ใน C # generics นิยามประเภททั่วไปจะถูกเก็บไว้ในหน่วยความจำขณะใช้งานจริง เมื่อใดก็ตามที่จำเป็นต้องมีประเภทคอนกรีตใหม่สภาพแวดล้อมรันไทม์รวมคำจำกัดความประเภททั่วไปและข้อโต้แย้งประเภทและสร้างประเภทใหม่ (reification) ดังนั้นเราจึงได้รับรูปแบบใหม่สำหรับการรวมกันของการขัดแย้งประเภทแต่ละที่รันไทม์

    1. เทคนิคการใช้งานนี้อนุญาตให้มีการผสมอาร์กิวเมนต์ประเภทใดก็ได้ การใช้ประเภทค่าเป็นอาร์กิวเมนต์ประเภททั่วไปจะไม่ทำให้เกิดมวยเนื่องจากประเภทเหล่านี้จะนำไปใช้เอง ( มวยยังคงอยู่ใน C #แน่นอน - แต่มันเกิดขึ้นในสถานการณ์อื่นไม่ใช่อันนี้)
    2. การทำสำเนารหัสอาจเป็นปัญหา - แต่ในทางปฏิบัติแล้วไม่ใช่เพราะการใช้งานอย่างฉลาดพอ ( รวมถึง Microsoft. NETและMono ) สามารถแชร์รหัสสำหรับอินสแตนซ์ได้
    3. ข้อมูลประเภทได้รับการปรับปรุงซึ่งช่วยให้มีความเชี่ยวชาญในขอบเขตโดยการตรวจสอบข้อโต้แย้งประเภทโดยใช้การสะท้อน อย่างไรก็ตามระดับของความเชี่ยวชาญมี จำกัด เนื่องจากข้อเท็จจริงที่ว่าคำจำกัดความชนิดทั่วไปถูกรวบรวมไว้ก่อนการทำให้เกิดขึ้นใหม่ใด ๆ (สิ่งนี้กระทำโดยการรวบรวมคำนิยามกับข้อ จำกัด ของพารามิเตอร์ประเภท - ดังนั้นคอมไพเลอร์จะต้องสามารถ "เข้าใจ" คำจำกัดความแม้ในกรณีที่ไม่มีข้อโต้แย้งประเภทเฉพาะ )
    4. เทคนิคการใช้งานนี้ขึ้นอยู่กับการสนับสนุนรันไทม์และการรวบรวม JIT (ซึ่งเป็นสาเหตุที่คุณมักได้ยินว่าC # generics มีข้อ จำกัด บางประการเกี่ยวกับแพลตฟอร์มเช่น iOSซึ่งการสร้างโค้ดแบบไดนามิกถูก จำกัด )
    5. ในบริบทของ C # generics การทำซ้ำจะทำเพื่อคุณโดยสภาพแวดล้อมรันไทม์ อย่างไรก็ตามถ้าคุณต้องการที่จะเข้าใจความแตกต่างระหว่างคำจำกัดความของประเภททั่วไปและประเภททั่วไปที่เป็นรูปธรรมมากขึ้นคุณสามารถทำการทำให้เป็นจริงได้ด้วยตัวเองโดยใช้System.Typeคลาส (แม้ว่าชุดอาร์กิวเมนต์ประเภททั่วไปที่เฉพาะเจาะจง ไม่ปรากฏในซอร์สโค้ดของคุณโดยตรง)
  • ในเทมเพลต C ++ การกำหนดเทมเพลตจะถูกเก็บรักษาไว้ในหน่วยความจำในเวลารวบรวม เมื่อใดก็ตามที่ต้องการอินสแตนซ์ใหม่ของประเภทเทมเพลตในซอร์สโค้ดคอมไพเลอร์จะรวมคำนิยามเทมเพลตและอาร์กิวเมนต์เทมเพลตและสร้างประเภทใหม่ ดังนั้นเราจึงได้รับชนิดที่ไม่ซ้ำสำหรับการรวมกันของการขัดแย้งแม่แบบแต่ละที่รวบรวมเวลา

    1. เทคนิคการใช้งานนี้อนุญาตให้มีการผสมอาร์กิวเมนต์ประเภทใดก็ได้
    2. เป็นที่ทราบกันดีว่าการทำสำเนาไบนารีโค้ด แต่เครื่องมือโซ่สมาร์ทที่เพียงพอยังคงสามารถตรวจจับสิ่งนี้และแบ่งปันรหัสสำหรับอินสแตนซ์บางอย่าง
    3. นิยามแม่แบบตัวเองไม่ได้ "รวบรวม" - เพียง instantiations คอนกรีตเป็นข้อมูลที่รวบรวมจริง ตำแหน่งนี้ข้อ จำกัด น้อยลงในคอมไพเลอร์และช่วยให้ระดับสูงของแม่แบบเชี่ยวชาญ
    4. เนื่องจากการดำเนินการอินสแตนซ์ของเท็มเพลตในเวลารวบรวมจึงไม่ต้องการการสนับสนุนรันไทม์ที่นี่
    5. กระบวนการนี้ถูกเรียกว่าmonomorphizationโดยเฉพาะในชุมชน Rust คำนี้ใช้ในทางตรงกันข้ามกับตัวแปรหลากหลายซึ่งเป็นชื่อของแนวคิดที่มาจาก generics

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

3
นอกจากนี้ใน C ++ สิ่งนี้จะช่วยแนะนำเทมเพลตเฉพาะทางซึ่งแต่ละประเภทคอนกรีต (หรือบางส่วน) สามารถมีการใช้งานที่แตกต่างกัน เห็นได้ชัดว่าเป็นไปไม่ได้ใน Java แต่ไม่ใช่ใน C #
quetzalcoatl

@quetzalcoatl แม้ว่าเหตุผลหนึ่งในการใช้นั่นคือการลดจำนวนรหัสที่ผลิตด้วยประเภทตัวชี้และ C # ทำสิ่งที่เทียบเคียงได้กับประเภทการอ้างอิงที่อยู่เบื้องหลัง ถึงกระนั้นนั่นก็เป็นเพียงเหตุผลเดียวที่ใช้มันและมีบางครั้งที่ความเชี่ยวชาญด้านเทมเพลตจะดี
Jon Hanna

สำหรับ Java คุณอาจต้องการเพิ่มว่าในขณะที่ข้อมูลประเภทจะถูกลบ casts จะถูกเพิ่มโดยคอมไพเลอร์ทำให้ bytecode ไม่สามารถแยกแยะได้จาก pre-generics bytecode
แกนสนิม

27

การทำให้เป็นจริงหมายถึงโดยทั่วไป (นอกวิทยาศาสตร์คอมพิวเตอร์) "เพื่อทำให้บางสิ่งเป็นจริง"

ในการเขียนโปรแกรมเป็นสิ่งที่reifiedถ้าเราสามารถที่จะเข้าถึงข้อมูลเกี่ยวกับเรื่องนี้ในภาษาของตัวเอง

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

โดยทั่วไปภาษา OO มีวิธีการ (และหลาย ๆ อย่างที่ไม่มีฟังก์ชั่นที่คล้ายกัน แต่ไม่ได้ผูกไว้กับคลาส) เช่นนี้คุณสามารถกำหนดวิธีการในภาษาดังกล่าวเรียกมันว่าอาจจะแทนที่มันและอื่น ๆ ไม่ใช่ทุกภาษาที่ให้คุณจัดการกับวิธีการดังกล่าวเป็นข้อมูลในโปรแกรมได้ C # (และจริงๆแล้ว. NET แทนที่จะเป็น C #) ช่วยให้คุณสามารถใช้ประโยชน์จากMethodInfoวัตถุที่เป็นตัวแทนของวิธีการดังนั้นในวิธีการ C # จะมีการปรับใหม่ วิธีการใน C # คือ "วัตถุชั้นหนึ่ง"

ทุกภาษาในทางปฏิบัติมีวิธีการบางอย่างในการเข้าถึงหน่วยความจำของคอมพิวเตอร์ ในภาษาระดับต่ำเช่น C เราสามารถจัดการกับการแมประหว่างที่อยู่ตัวเลขที่ใช้โดยคอมพิวเตอร์โดยตรงดังนั้นการกดไลค์int* ptr = (int*) 0xA000000; *ptr = 42;นั้นสมเหตุสมผล (ตราบใดที่เรามีเหตุผลที่ดีที่จะสงสัยว่าการเข้าถึงที่อยู่หน่วยความจำ0xA000000ด้วยวิธีนี้จะชนะ ' ไม่ระเบิดอะไร) ใน C # นี้ไม่สมเหตุสมผล (เราสามารถบังคับได้ใน. NET แต่ด้วยการจัดการหน่วยความจำ. NET ที่เคลื่อนย้ายสิ่งต่าง ๆ รอบตัวมันไม่น่าจะมีประโยชน์มาก) C # ไม่ได้แก้ไขที่อยู่หน่วยความจำใหม่

ดังนั้นในขณะที่การปฏิเสธหมายถึง "ทำให้เป็นจริง" "ประเภทการแก้ไข" เป็นประเภทที่เราสามารถ "พูดถึง" ในภาษาที่เป็นปัญหาได้

ในยาชื่อสามัญนี้หมายถึงสองสิ่ง

หนึ่งคือนั่นList<string>คือประเภทเช่นเดียวกับstringหรือintเป็น เราสามารถเปรียบเทียบประเภทนั้นได้ชื่อและสอบถามเกี่ยวกับมัน:

Console.WriteLine(typeof(List<string>).FullName); // System.Collections.Generic.List`1[[System.String, mscorlib, Version=4.0.0.0, Culture=neutral, PublicKeyToken=b77a5c561934e089]]
Console.WriteLine(typeof(List<string>) == (42).GetType()); // False
Console.WriteLine(typeof(List<string>) == Enumerable.Range(0, 1).Select(i => i.ToString()).ToList().GetType()); // True
Console.WriteLine(typeof(List<string>).GenericTypeArguments[0] == typeof(string)); // True

ผลที่ตามมาคือเราสามารถ "พูดถึง" ประเภทของพารามิเตอร์ (หรือวิธีของคลาสทั่วไป) ของเมธอดภายในเมธอดเอง:

public static void DescribeType<T>(T element)
{
  Console.WriteLine(typeof(T).FullName);
}
public static void Main()
{
  DescribeType(42);               // System.Int32
  DescribeType(42L);              // System.Int64
  DescribeType(DateTime.UtcNow);  // System.DateTime
}

ตามกฎแล้วการทำเช่นนี้มากเกินไปคือ "ส่งกลิ่น" แต่มีหลายกรณีที่มีประโยชน์ ตัวอย่างเช่นดูที่:

public static TSource Min<TSource>(this IEnumerable<TSource> source)
{
  if (source == null) throw Error.ArgumentNull("source");
  Comparer<TSource> comparer = Comparer<TSource>.Default;
  TSource value = default(TSource);
  if (value == null)
  {
    using (IEnumerator<TSource> e = source.GetEnumerator())
    {
      do
      {
        if (!e.MoveNext()) return value;
        value = e.Current;
      } while (value == null);
      while (e.MoveNext())
      {
        TSource x = e.Current;
        if (x != null && comparer.Compare(x, value) < 0) value = x;
      }
    }
  }
  else
  {
    using (IEnumerator<TSource> e = source.GetEnumerator())
    {
      if (!e.MoveNext()) throw Error.NoElements();
      value = e.Current;
      while (e.MoveNext())
      {
        TSource x = e.Current;
        if (comparer.Compare(x, value) < 0) value = x;
      }
    }
  }
  return value;
}

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

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

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


คุณคิดว่าไม่สามารถทำสิ่งที่Minมีประโยชน์ข้างต้นได้? มันยากมากที่จะทำตามพฤติกรรมที่บันทึกไว้เป็นอย่างอื่น
Jon Hanna

ฉันคิดว่าข้อผิดพลาดเป็นพฤติกรรมที่ไม่ได้จัดทำเป็นเอกสารและมีความหมายว่าพฤติกรรมนั้นมีประโยชน์ (นอกเหนือจากกันพฤติกรรมของEnumerable.Min<TSource>ความแตกต่างคือไม่ได้โยนประเภทที่ไม่มีการอ้างอิงไว้ในคอลเลกชันที่ว่างเปล่า แต่คืนค่าเริ่มต้น (TSource) และมีการบันทึกไว้เป็น "คืนค่าต่ำสุดในลำดับทั่วไปเท่านั้น" ฉันจะโต้แย้งว่าทั้งคู่ควรโยนคอลเล็กชันที่ว่างเปล่าหรือควรจะส่งองค์ประกอบ "ศูนย์" เป็นพื้นฐานและผู้เปรียบเทียบ / ฟังก์ชั่นการเปรียบเทียบควรส่งผ่านเสมอ)
Martijn

1
นั่นจะมีประโยชน์น้อยกว่า Min ในปัจจุบันซึ่งตรงกับพฤติกรรม db ทั่วไปในประเภท nullable โดยไม่พยายามเป็นไปไม่ได้ในประเภท non-nullable (ความคิดพื้นฐานไม่ได้เป็นไปไม่ได้ แต่ไม่มีประโยชน์มากเว้นแต่จะมีค่าที่คุณสามารถรู้ได้ว่าจะไม่เกิดขึ้นในแหล่งที่มา)
Jon Hanna

1
การทำสิ่งต่าง ๆจะเป็นชื่อที่ดีกว่าสำหรับสิ่งนี้ :)
tchrist

@trist สิ่งที่สามารถเป็นจริง
Jon Hanna

15

ดังที่duffymo ตั้งข้อสังเกตไว้ว่า "การทำให้เป็นจริง" ไม่ใช่ความแตกต่างที่สำคัญ

ใน Java generics นั้นโดยทั่วไปจะมีการปรับปรุงการสนับสนุนเวลาคอมไพล์ - ช่วยให้คุณใช้พิมพ์ที่รุนแรงเช่นคอลเลกชันในรหัสของคุณและมีการจัดการความปลอดภัยประเภทสำหรับคุณ อย่างไรก็ตามสิ่งนี้มีอยู่เฉพาะในเวลาคอมไพล์ - โค้ดไบต์ที่คอมไพล์ไม่มีความคิดทั่วไปอีกต่อไป; ประเภททั่วไปทั้งหมดจะถูกเปลี่ยนเป็นประเภท "คอนกรีต" (ใช้objectถ้าประเภททั่วไปไม่ได้ จำกัด ) เพิ่มการแปลงประเภทและการตรวจสอบประเภทตามที่ต้องการ

ใน. NET generics เป็นคุณสมบัติที่สำคัญของ CLR เมื่อคุณคอมไพล์ประเภทสามัญมันยังคงอยู่ทั่วไปใน IL สร้างขึ้น มันไม่ได้แปลงเป็นรหัสที่ไม่ใช่แบบทั่วไปเหมือนใน Java

สิ่งนี้มีผลกระทบหลายประการต่อการทำงานของยาสามัญในทางปฏิบัติ ตัวอย่างเช่น:

  • Java SomeType<?>ต้องอนุญาตให้คุณผ่านการใช้งานประเภททั่วไปที่กำหนดอย่างเป็นรูปธรรม C # ไม่สามารถทำเช่นนี้ - ทุกที่เฉพาะเจาะจง ( reified ) ประเภททั่วไปเป็นชนิดของตัวเอง
  • objectประเภททั่วไปมากมายในชวาหมายความว่าค่าของพวกเขาจะถูกเก็บไว้ในฐานะที่เป็น สิ่งนี้สามารถมีผลกระทบต่อประสิทธิภาพเมื่อใช้ประเภทค่าในข้อมูลทั่วไป ใน C # เมื่อคุณใช้ประเภทค่าในประเภททั่วไปมันจะยังคงเป็นประเภทค่า

เพื่อให้ตัวอย่างสมมติว่าคุณมีListประเภททั่วไปที่มีอาร์กิวเมนต์ทั่วไปหนึ่งรายการ ใน Java List<String>และList<Int>จะจบลงด้วยการเป็นชนิดเดียวกันที่รันไทม์ - ประเภททั่วไปมีอยู่จริงๆสำหรับรหัสเวลาคอมไพล์ ทุกสายไปยังเช่นGetValueจะถูกเปลี่ยนเป็น(String)GetValueและ(Int)GetValueตามลำดับ

ใน C # List<string>และList<int>มีสองประเภทที่แตกต่างกัน พวกเขาจะไม่สามารถใช้แทนกันได้และความปลอดภัยประเภทของพวกเขาจะถูกบังคับใช้ในรันไทม์เช่นกัน ไม่ว่าคุณจะทำอะไรnew List<int>().Add("SomeString")จะไม่ทำงาน - ที่เก็บข้อมูลพื้นฐานในList<int>นั้นเป็นจำนวนเต็มจริง ๆในขณะที่ใน Java มันจำเป็นต้องมีobjectอาร์เรย์ ใน C # ไม่มีการปลดเปลื้องที่เกี่ยวข้องไม่มีการชกมวย ฯลฯ

นอกจากนี้ยังควรจะทำให้มันชัดเจนว่าทำไม C # ไม่สามารถทำสิ่งเดียวกันเช่น Java SomeType<?>กับ ใน Java ประเภททั่วไปทั้งหมด "ได้รับมาจาก" SomeType<?>จบลงด้วยการเป็นประเภทเดียวกันที่แน่นอน ใน C # ทุกSomeType<T>s เฉพาะต่าง ๆเป็นของตัวเองแยกประเภท การลบการตรวจสอบเวลาคอมไพล์อาจเป็นไปได้ที่จะผ่านSomeType<Int>แทนSomeType<String>(และที่จริงแล้วนั่นSomeType<?>หมายถึง "ละเว้นการตรวจสอบเวลาคอมไพล์สำหรับประเภททั่วไปที่กำหนด") ใน C # เป็นไปไม่ได้แม้แต่กับประเภทที่ได้รับมา (นั่นคือคุณไม่สามารถทำได้List<object> list = (List<object>)new List<string>();แม้ว่าstringจะได้มาจากobject)

การใช้งานทั้งสองมีข้อดีและข้อเสียของพวกเขา มีหลายครั้งที่ฉันชอบที่จะยอมให้SomeType<?>เป็นข้อโต้แย้งใน C # - แต่มันก็ไม่สมเหตุสมผลที่ C # generics ทำงาน


2
ดีที่คุณสามารถใช้ประโยชน์จากประเภทList<>, Dictionary<,>และอื่น ๆ ใน C # แต่ช่องว่างระหว่างที่และรายการคอนกรีตที่กำหนดหรือพจนานุกรมจะใช้เวลาค่อนข้างบิตของการสะท้อนไปที่สะพาน ความแปรปรวนบนอินเทอร์เฟซจะช่วยในบางกรณีที่ครั้งหนึ่งเราอาจต้องการเชื่อมช่องว่างนั้นได้ง่าย แต่ไม่ใช่ทั้งหมด
Jon Hanna

2
@JonHanna คุณสามารถใช้List<>เพื่อยกตัวอย่างประเภททั่วไปที่เฉพาะเจาะจงใหม่ - แต่ก็ยังหมายถึงการสร้างประเภทเฉพาะที่คุณต้องการ แต่คุณไม่สามารถใช้List<>เป็นอาร์กิวเมนต์ได้ แต่ใช่อย่างน้อยนี่ก็ช่วยให้คุณลดช่องว่างโดยใช้การสะท้อน
Luaan

.NET Framework มีข้อ จำกัด ทั่วไปแบบตายตัวสามข้อที่ไม่ใช่ประเภทของที่เก็บข้อมูล ข้อ จำกัด อื่น ๆ ทั้งหมดจะต้องเป็นประเภทสถานที่จัดเก็บ นอกจากนี้ครั้งเพียงประเภททั่วไปTสามารถตอบสนองข้อ จำกัด การจัดเก็บข้อมูลสถานที่ประเภทUเมื่อTและUเป็นประเภทเดียวกันหรือเป็นชนิดที่สามารถถืออ้างอิงถึงตัวอย่างของU Tมันเป็นไปไม่ได้ที่จะมีความหมายว่ามีที่เก็บข้อมูลประเภทSomeType<?>แต่ในทางทฤษฎีแล้วเป็นไปได้ที่จะมีข้อ จำกัด ทั่วไปของประเภทนั้น
supercat

1
มันไม่เป็นความจริงเลยที่ Java bytecode ที่คอมไพล์แล้วไม่มีความคิดเรื่อง generics มันเป็นเพียงอินสแตนซ์ของคลาสที่ไม่มีความคิดทั่วไป นี่คือความแตกต่างที่สำคัญ ฉันเคยเขียนเกี่ยวกับเรื่องนี้ที่programmers.stackexchange.com/questions/280169/…ถ้าคุณสนใจ
ruakh

2

Reification เป็นแนวคิดการสร้างแบบจำลองเชิงวัตถุ

Reify เป็นคำกริยาที่หมายถึง"ทำให้สิ่งที่เป็นนามธรรมที่แท้จริง" "ทำให้สิ่งที่เป็นนามธรรมของจริง"

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

นอกจากนี้ยังเป็นเรื่องธรรมดาที่จะเปลี่ยนแนวคิดนามธรรมเป็นองค์ประกอบเช่นกัน (WindowListener, Broker, ฯลฯ )


2
การทำให้เป็นจริงเป็นแนวคิดทั่วไปของ "การทำให้บางสิ่งบางอย่างเป็นจริง" ซึ่งแม้ว่ามันจะนำไปใช้กับการสร้างแบบจำลองเชิงวัตถุตามที่คุณพูด แต่ก็มีความหมายในบริบทของการใช้งานข้อมูลทั่วไป
Jon Hanna

2
ดังนั้นฉันจึงได้รับการศึกษาโดยการอ่านคำตอบเหล่านี้ ฉันจะแก้ไขคำตอบของฉัน
duffymo

2
คำตอบนี้ไม่ได้ทำอะไรเพื่อตอบสนองความสนใจของ OP ในเรื่องยาชื่อสามัญและตัวแปรหลากหลาย
Erick G. Hagstrom

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

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