ส่งวัตถุไปที่ T


91

ฉันกำลังแยกวิเคราะห์ไฟล์ XML กับXmlReaderคลาสใน. NET และฉันคิดว่าการเขียนฟังก์ชันการแยกวิเคราะห์ทั่วไปเพื่ออ่านแอตทริบิวต์ต่างๆโดยทั่วไปจะเป็นการดี ฉันมาพร้อมกับฟังก์ชั่นต่อไปนี้:

private static T ReadData<T>(XmlReader reader, string value)
{
    reader.MoveToAttribute(value);
    object readData = reader.ReadContentAsObject();
    return (T)readData;
}

เมื่อฉันตระหนักได้ว่าสิ่งนี้ไม่ได้ผลทั้งหมดตามที่ฉันวางแผนไว้ มันแสดงข้อผิดพลาดกับประเภทดั้งเดิมเช่นintหรือdoubleเนื่องจากนักแสดงไม่สามารถแปลงจากประเภทstringเป็นตัวเลขเป็นประเภทตัวเลขได้ มีวิธีใดบ้างที่ฟังก์ชันของฉันจะเหนือกว่าในรูปแบบที่แก้ไข

คำตอบ:


209

ก่อนอื่นให้ตรวจสอบว่าสามารถแคสได้หรือไม่

if (readData is T) {
    return (T)readData;
} 
try {
   return (T)Convert.ChangeType(readData, typeof(T));
} 
catch (InvalidCastException) {
   return default(T);
}

1
ฉันเปลี่ยนบรรทัดด้วย Convert.ChangeType เป็น: 'return (T) Convert.ChangeType (readData, typeof (T), System.Globalization.CultureInfo.InstalledUICulture.NumberFormat) เพื่อให้ทำงานกับการกำหนดค่าทางวัฒนธรรมต่างๆ
Kasper Holdum

2
นี่คือคำตอบที่ถูกต้อง แต่ฉันสามารถโต้แย้งได้ว่า try / catch นั้นซ้ำซ้อนโดยสิ้นเชิงที่นี่ โดยเฉพาะอย่างยิ่งเมื่อพิจารณาถึงข้อยกเว้นที่ปิดเสียง ฉันคิดว่าส่วน if (readData is T) {... } เป็นความพยายามที่เพียงพอ
PIM

คุณสามารถตรวจสอบว่า readDate เป็นโมฆะก่อนที่จะแปลง ถ้าเป็นเช่นนั้นให้คืนค่าเริ่มต้น (T)
Manuel Koch

ฉันได้รับ "Object ต้องใช้ IConvertible"
Casey Crookston

19

คุณลองConvert.ChangeType แล้วหรือยัง?

หากเมธอดส่งคืนสตริงเสมอซึ่งฉันคิดว่าคี่ แต่นั่นเป็นสิ่งที่นอกเหนือจากจุดนั้นบางทีโค้ดที่เปลี่ยนแปลงนี้อาจทำในสิ่งที่คุณต้องการ:

private static T ReadData<T>(XmlReader reader, string value)
{
    reader.MoveToAttribute(value);
    object readData = reader.ReadContentAsObject();
    return (T)Convert.ChangeType(readData, typeof(T));
}

ตอนแรกฉันได้ดู Convert.ChangeType แต่คิดว่ามันไม่มีประโยชน์สำหรับการดำเนินการนี้ด้วยเหตุผลบางประการ คุณและ Bob ต่างให้คำตอบเหมือนกันและฉันตัดสินใจที่จะผสมผสานระหว่างคำตอบของคุณดังนั้นฉันจึงหลีกเลี่ยงการใช้คำสั่ง try แต่ยังคงใช้ 'return (T) readData' เมื่อเป็นไปได้
Kasper Holdum


3

คุณอาจกำหนดให้ประเภทเป็นประเภทอ้างอิง:

 private static T ReadData<T>(XmlReader reader, string value) where T : class
 {
     reader.MoveToAttribute(value);
     object readData = reader.ReadContentAsObject();
     return (T)readData;
 }

จากนั้นทำอย่างอื่นที่ใช้ประเภทค่าและ TryParse ...

 private static T ReadDataV<T>(XmlReader reader, string value) where T : struct
 {
     reader.MoveToAttribute(value);
     object readData = reader.ReadContentAsObject();
     int outInt;
     if(int.TryParse(readData, out outInt))
        return outInt
     //...
 }

3

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

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

ลองใช้ ReadContentAs แทนซึ่งเป็นสิ่งที่คุณต้องการจริงๆ

private static T ReadData<T>(XmlReader reader, string value)
{
    reader.MoveToAttribute(value);
    object readData = reader.ReadContentAs(typeof(T), null);
    return (T)readData;
}

ดูค่อนข้างกะทัดรัดและหรูหรา อย่างไรก็ตามวิธีแก้ปัญหาในคำตอบที่ยอมรับนั้นใช้ ChangeType ซึ่งเข้ากันได้กับหลายวัฒนธรรมเนื่องจากยอมรับ IFormatProvider เนื่องจากนี่เป็นความจำเป็นสำหรับโครงการฉันจะดำเนินการแก้ไขต่อไป
Kasper Holdum

2

คุณสามารถพาสอินเป็นพารามิเตอร์ตัวแทนซึ่งจะแปลงจากสตริงเป็น T.


1

เพิ่มข้อ จำกัด 'คลาส' (หรือมีรายละเอียดมากขึ้นเช่นคลาสพื้นฐานหรืออินเทอร์เฟซของอ็อบเจ็กต์ T exepected):

private static T ReadData<T>(XmlReader reader, string value) where T : class
{
    reader.MoveToAttribute(value);
    object readData = reader.ReadContentAsObject();
    return (T)readData;
}

หรือwhere T : IMyInterfaceหรือwhere T : new()ฯลฯ


1

อันที่จริงคำตอบทำให้เกิดคำถามที่น่าสนใจซึ่งเป็นสิ่งที่คุณต้องการให้ฟังก์ชันของคุณทำในกรณีที่เกิดข้อผิดพลาด

บางทีมันอาจจะสมเหตุสมผลกว่าที่จะสร้างมันในรูปแบบของเมธอด TryParse ที่พยายามอ่านเป็น T แต่จะส่งคืนเท็จหากไม่สามารถทำได้?

    private static bool ReadData<T>(XmlReader reader, string value, out T data)
    {
        bool result = false;
        try
        {
            reader.MoveToAttribute(value);
            object readData = reader.ReadContentAsObject();
            data = readData as T;
            if (data == null)
            {
                // see if we can convert to the requested type
                data = (T)Convert.ChangeType(readData, typeof(T));
            }
            result = (data != null);
        }
        catch (InvalidCastException) { }
        catch (Exception ex)
        {
            // add in any other exception handling here, invalid xml or whatnot
        }
        // make sure data is set to a default value
        data = (result) ? data : default(T);
        return result;
    }

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

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