เหตุใด C # จึงไม่สามารถเปรียบเทียบประเภทของวัตถุสองชนิดด้วยกัน แต่ VB ไม่ได้


152

ฉันมีสองวัตถุใน C # และไม่ทราบว่าเป็นบูลีนหรือประเภทอื่น ๆ อย่างไรก็ตามเมื่อฉันพยายามเปรียบเทียบ C # เหล่านั้นล้มเหลวในการให้คำตอบที่ถูกต้อง ฉันได้ลองใช้รหัสเดียวกันกับ VB.NET แล้วและได้ทำเช่นนั้น!

ใครช่วยบอกฉันได้ว่าจะแก้ไขได้อย่างไรถ้ามีทางออก?

ค#:

object a = true;
object b = true;
object c = false;
if (a == b) c = true;
MessageBox.Show(c.ToString()); //Outputs False !!

VB.NET:

Dim a As Object = True
Dim b As Object = True
Dim c As Object = False
If (a = b) Then c = True
MessageBox.Show(c.ToString()) '// Outputs True

3
สิ่งที่ถ้าคุณเปลี่ยน Comparer เท่าเทียมกันเพื่อa.Equals(b)?
Jason Meckley

8
นี่เป็นคำถามที่ดีสำหรับวัตถุประสงค์การสอน
Lobo

10
เนื่องจากรหัส VB.NET ของคุณไม่เท่ากับรหัส C # ของคุณ
Security Hound

9
เมื่อคุณกำหนดให้คุณได้รับมวยและสร้างกล่องที่มีa trueเมื่อคุณกำหนดให้bคุณได้รับอีกtrueกล่องนอกจากนี้ยังมี เมื่อคุณเปรียบเทียบaและbเนื่องจากทั้งคู่เป็นประเภทเวลาคอมไพล์objectคุณจึงเรียกโอเวอร์โหลดที่operator ==(object, object)กำหนดโดยข้อกำหนดภาษา C # โอเวอร์โหลดนี้ตรวจสอบเพื่อดูว่าการอ้างอิงไปที่วัตถุเดียวกันหรือไม่ เนื่องจากคุณมีสองช่องผลลัพธ์คือfalseและคำสั่ง "ใต้" คุณifจะไม่ทำงาน เพื่อให้เข้าใจสิ่งนี้ดีขึ้นลองเปลี่ยนการมอบหมายbเป็น: object b = a;ตอนนี้คุณมีเพียงหนึ่งช่อง
Jeppe Stig Nielsen

3
ฉันมีโอกาสก่อนที่จะพูดว่า "ระวังให้ดีสมมติว่า VB.NET และ C # เป็นภาษาเดียวกันที่พูดด้วยสำเนียงที่แตกต่างกัน - พวกเขาไม่ใช่คน"
AakashM

คำตอบ:


168

ใน C # ที่==ผู้ประกอบการ (เมื่อนำไปใช้กับชนิดการอ้างอิงการแสดงออก) ดำเนินการอ้างอิงการตรวจสอบความเท่าเทียมกันเว้นแต่จะมากเกินไป คุณกำลังเปรียบเทียบการอ้างอิงสองรายการซึ่งเป็นผลมาจากการแปลงมวยดังนั้นการอ้างอิงเหล่านั้นจึงแตกต่างกัน

แก้ไข: ด้วยประเภทที่เกินพิกัด==คุณสามารถรับพฤติกรรมที่แตกต่าง - แต่ขึ้นอยู่กับประเภทเวลารวบรวมของการแสดงออก ตัวอย่างเช่นstringให้==(string, string):

string x = new string("foo".ToCharArray());
string y = new string("foo".ToCharArray());
Console.WriteLine(x == y); // True
Console.WriteLine((object) x == (object) y); // False

นี่คือการเปรียบเทียบครั้งแรกที่ใช้ตัวดำเนินการโอเวอร์โหลด แต่ตัวที่สองกำลังใช้การเปรียบเทียบการอ้างอิง "ค่าเริ่มต้น"

ใน VB ตัว=ดำเนินการทำงานได้มากขึ้นทั้งหมด - ไม่เพียงเทียบเท่ากับการใช้object.Equals(x, y)เนื่องจากสิ่งต่าง ๆ เช่นOption Compareมีผลต่อการเปรียบเทียบข้อความ

โดยพื้นฐานแล้วผู้ประกอบการจะไม่ทำงานในลักษณะเดียวกันและไม่ได้ตั้งใจให้ทำงานแบบเดียวกัน


17
+1 ผมรู้ว่าคุณจะเป็นรอบที่คุณรักชนิดของคำถามเหล่านี้ลึกลับ :)
Abdusalam เบนฮัจย์

3
@AbZy: ฉันหวังว่าจะสามารถให้รายละเอียดเพิ่มเติมเกี่ยวกับสิ่งที่=ทำใน VB แต่สเป็คไม่ชัดเจนอย่างมาก
Jon Skeet

สิ่งที่น่าสนใจ แต่การเปลี่ยนวัตถุให้เป็นแบบไดนามิกนั้นเหมือนกับ VB
VladL

4
@VladL: ใช่แล้วมันจะดำเนินการตามประเภทเวลาดำเนินการและทำการbool == boolเปรียบเทียบ
Jon Skeet

1
@Mahdi Lobo อาจให้รหัสไว้ แต่คำตอบของเขาก็ผิดเช่นเดียวกับ Jon
บริการ

79

นอกจากคำตอบของจอนซึ่งอธิบายด้าน C # ของสิ่งต่าง ๆ แล้วนี่คือสิ่งที่ VB ทำ:

ใน VB ด้วยOption Strict Onการเปรียบเทียบผ่านการทดสอบ= เสมอสำหรับความเท่าเทียมกันของค่าและไม่เคยสำหรับความเท่าเทียมกันอ้างอิง ในความเป็นจริงรหัสของคุณไม่ได้รวบรวมเมื่อคุณเปลี่ยนOption Strict Onเพราะไม่ได้กำหนดSystem.Object Operator=คุณควรมีตัวเลือกนี้อยู่เสมอมันจับแมลงได้อย่างมีประสิทธิภาพมากกว่าวีนัส flytrap (แม้ว่าในกรณีของคุณพฤติกรรมแบบหละหลวมนี้จะทำในสิ่งที่ถูกต้อง) 1

ในความเป็นจริงกับOption Strict OnVB ทำงานได้แม้จะเข้มงวดกว่า C #: ใน C # a == b ทั้งเรียกการโทรSomeType.operator==(a, b)หรือถ้าไม่มีอยู่เรียกการเปรียบเทียบความเท่าเทียมกันอ้างอิง (ซึ่งเทียบเท่ากับการโทรobject.ReferenceEquals(a, b))

ใน VB ในอีกทางหนึ่งการเปรียบเทียบa = b จะเรียกใช้ตัวดำเนินการความเสมอภาคเสมอ 2หากคุณต้องการใช้การเปรียบเทียบความเท่าเทียมกันของการอ้างอิงคุณต้องใช้a Is b(ซึ่งก็คือเหมือนกันอีกครั้งObject.ReferenceEquals(a, b))


1)ต่อไปนี้เป็นข้อบ่งชี้ที่ดีว่าทำไมใช้Option Strict Offเป็นความคิดที่ไม่ดี: ฉัน VB.NET มือเกือบทศวรรษที่ผ่านมาจากก่อนที่จะปล่อยอย่างเป็นทางการ .NET จนกระทั่งไม่กี่ปีที่ผ่านมาและฉันได้อย่างคิดว่าสิ่งที่ทำกับa = b Option Strict Offมันเปรียบเทียบความเท่าเทียมกันบางอย่าง แต่เกิดอะไรขึ้นกันแน่และทำไมไม่มีความคิด มันซับซ้อนกว่าdynamicฟีเจอร์ของ C # แต่เนื่องจาก (ขึ้นอยู่กับ API ที่มีเอกสารครบถ้วน) นี่คือสิ่งที่ MSDN พูดว่า:

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

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


4
+1 ฉันคิดว่านี่เป็นกรณีที่ผู้ออกแบบ VB.NET ประสบความสำเร็จในการสร้างภาษา "เพียงแค่ทำงาน" สำหรับโปรแกรมเมอร์ที่มาจาก VB6 และ VBA ที่ OOP โดดเด่นน้อยกว่ามากและแนวคิดเรื่องความเท่าเทียมกันของการอ้างอิงมีความสำคัญน้อยกว่ามาก รหัส coder VB สามารถเขียนโค้ดที่ใช้งานได้ดีโดยไม่ต้องคิดอะไรมากมายเกี่ยวกับวัตถุและอื่น ๆ
John M Gant

5
+1 นี่ไม่ได้ถูกอัปโหลดมากเท่าที่ควร การไม่ได้ใช้Option Strict Onจะถือว่าเป็นความผิดทางอาญา ...
Deer Hunter

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

4

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

ลองสิ่งนี้:

public class MyObject
{
    public MyObject(String v)
    {
        Value = v;
    }
    public String Value { get; set; }
}

MyObject a = new MyObject("a");
MyObject b = new MyObject("a");
if(a==b){
    Debug.WriteLine("a reference is equal to b reference");
}else{
    Debug.WriteLine("a reference is not equal to b reference");
}
if (a.Equals(b)) {
    Debug.WriteLine("a object is equal to b object");
} else {
    Debug.WriteLine("a object is not equal to b object");
}

ผล:

a reference is not equal to b reference
a object is not equal to b object

ตอนนี้ลองทำสิ่งนี้:

public class MyObject
{
    public MyObject(String v)
    {
        Value = v;
    }
    public String Value { get; set; }

    public bool Equals(MyObject o)
    {
        return (Value.CompareTo(o.Value)==0);
    }
}
MyObject a = new MyObject("a");
MyObject b = new MyObject("a");
if(a==b){
    Debug.WriteLine("a reference is equal to b reference");
}else{
    Debug.WriteLine("a reference is not equal to b reference");
}
if (a.Equals(b)) {
    Debug.WriteLine("a object is equal to b object");
} else {
    Debug.WriteLine("a object is not equal to b object");
}

ผล:

a reference is not equal to b reference
a object is equal to b object

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

1
@Servy: โปรดทราบว่าคุณไม่สามารถแทนที่ ได้==- คุณสามารถโอเวอร์โหลดได้เท่านั้น
Jon Skeet

1
ขออภัย -1 คำตอบนี้ไม่ถูกต้องและไม่ควรเป็นคำตอบที่ยอมรับได้
Konrad Rudolph

ที่ไหนสักแห่งมีคำถาม Java รอคำตอบนี้
ชาด Schouggins

3

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

ขึ้นอยู่กับการรวบรวมเวลาที่พิมพ์คอมไพเลอร์จะตรวจสอบสิ่งที่การดำเนินการoperator ==กับการใช้งาน มันอาจใช้การเริ่มต้นobjectใช้งานมันอาจใช้หนึ่งในเกินพิกัดตัวเลขโดยภาษาหรืออาจเป็นการใช้งานที่ผู้ใช้กำหนด

สิ่งนี้แตกต่างจาก VB ใน VB นั้นไม่ได้กำหนดการใช้งานในเวลารวบรวม มันจะรอจนกระทั่งรันไทม์และตรวจสอบพารามิเตอร์ทั้งสองที่ได้รับเพื่อพิจารณา==ว่าควรใช้การปฏิบัติงานของโอเปอเรเตอร์ใด

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

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


ดูดีสำหรับ C #; ฉันไม่รู้เกี่ยวกับสิ่งที่=ใน VB พูดแน่นอน
Jon Skeet

@ JonSkeet ยุติธรรมเพียงพอ
บริการ

ต่อmsdn.microsoft.com/en-us/library/cey92b0t(v=vs.110).aspxในส่วน "การเขียนโปรแกรม typeless กับผู้ประกอบการเชิงเปรียบเทียบ" =พร้อมกับผู้ประกอบการเปรียบเทียบเชิงสัมพันธ์อื่น ๆ เช่น<, >=ฯลฯ Objectจะได้รับการดูแลเป็นพิเศษเมื่อทั้งสองหรือทั้งสองข้างของผู้ประกอบการคือ การรักษาพิเศษนี้ทำขึ้นเพื่อให้โปรแกรมเมอร์ VB6 ซึ่งคุ้นเคยกับการใช้ประเภทที่รู้จักกันVariantใน pre-.NET VB สามารถใช้ประโยชน์ได้Objectใน VB.Net ในรูปแบบที่พวกเขาเคยใช้มาVariantก่อน
rskar

ในการใส่อีกวิธีหนึ่งและปล่อยให้เอฟเฟกต์ของการรับน้ำหนักมากเกินไปและOption Strict OnVB =จะเอนเอียงไปทาง unboxing และObjectจนกว่ามันจะไปถึง String หรือตัวเลข
rskar
โดยการใช้ไซต์ของเรา หมายความว่าคุณได้อ่านและทำความเข้าใจนโยบายคุกกี้และนโยบายความเป็นส่วนตัวของเราแล้ว
Licensed under cc by-sa 3.0 with attribution required.