Transmisikan objek ke T


91

Saya XmlReadermem -parsing file XML dengan kelas di .NET dan saya pikir akan pintar menulis fungsi parse umum untuk membaca atribut yang berbeda secara umum. Saya datang dengan fungsi berikut:

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

Saat saya menyadari, ini tidak bekerja sepenuhnya seperti yang saya rencanakan; itu melontarkan kesalahan dengan tipe primitif seperti intatau double, karena cast tidak dapat mengonversi dari tipe a stringke numerik. Apakah ada cara agar fungsi saya berlaku dalam bentuk yang dimodifikasi?

Jawaban:


209

Pertama, periksa untuk melihat apakah bisa dilemparkan.

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

1
Saya mengubah baris dengan Convert.ChangeType menjadi: 'return (T) Convert.ChangeType (readData, typeof (T), System.Globalization.CultureInfo.InstalledUICulture.NumberFormat) untuk membuatnya bekerja pada berbagai konfigurasi budaya yang berbeda.
Kasper Holdum

2
Ini jawaban yang benar. Tapi saya bisa berargumen bahwa coba / tangkap sama sekali berlebihan di sini. Terutama mengingat pengecualian yang diredam. Saya pikir porsi if (readData is T) {...} adalah upaya yang cukup.
pim

Anda dapat memeriksa apakah readDate bernilai null sebelum mengonversinya. Jika demikian kembalikan default (T).
Manuel Koch

Saya mendapatkan "Objek harus mengimplementasikan IConvertible."
Casey Crookston

19

Sudahkah Anda mencoba Convert.ChangeType ?

Jika metode selalu mengembalikan string, yang menurut saya aneh, tetapi itu bukan intinya, mungkin kode yang diubah ini akan melakukan apa yang Anda inginkan:

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

Saya awalnya melihat Convert.ChangeType tetapi memutuskan itu tidak berguna untuk operasi ini karena beberapa alasan aneh. Anda dan Bob sama-sama memberikan jawaban yang sama, dan saya memutuskan untuk menggunakan campuran antara jawaban Anda jadi saya menghindari menggunakan pernyataan percobaan tetapi masih menggunakan 'return (T) readData' bila memungkinkan.
Kasper Holdum

11

mencoba

if (readData is T)
    return (T)(object)readData;

3

Anda dapat meminta tipe menjadi tipe referensi:

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

Dan kemudian lakukan yang lain yang menggunakan tipe nilai dan 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

Sebenarnya, masalah di sini adalah penggunaan ReadContentAsObject. Sayangnya, metode ini tidak memenuhi harapannya; sementara itu harus mendeteksi jenis yang paling tepat untuk nilai, itu sebenarnya mengembalikan string, tidak peduli apa (ini dapat diverifikasi menggunakan Reflector).

Namun, dalam kasus spesifik Anda, Anda sudah tahu tipe yang ingin Anda gunakan, oleh karena itu saya akan mengatakan Anda menggunakan metode yang salah.

Coba gunakan ReadContentAs sebagai gantinya, itulah yang Anda butuhkan.

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

Terlihat cukup kompak dan elegan. Namun, solusi dalam jawaban yang diterima menggunakan ChangeType yang kompatibel dengan berbagai budaya yang berbeda karena menerima IFormatProvider. Karena ini adalah kebutuhan untuk proyek, saya akan tetap menggunakan solusi itu.
Kasper Holdum

2

Anda mungkin dapat mengirimkan, sebagai parameter, delegasi yang akan mengkonversi dari string ke T.


1

Tambahkan batasan 'class' (atau lebih detail, seperti class dasar atau antarmuka objek T yang Anda pilih):

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

atau where T : IMyInterfaceatau where T : new(), dll


1

Sebenarnya, tanggapan tersebut memunculkan pertanyaan yang menarik, yaitu apa yang Anda ingin fungsi Anda lakukan jika terjadi kesalahan.

Mungkin akan lebih masuk akal untuk membangunnya dalam bentuk metode TryParse yang mencoba membaca menjadi T, tetapi mengembalikan false jika tidak dapat dilakukan?

    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;
    }

edit: sekarang setelah saya memikirkannya, apakah saya benar-benar perlu melakukan tes convert.changetype? bukankah garis as sudah mencoba melakukan itu? Saya tidak yakin bahwa melakukan pemanggilan changetype tambahan itu benar-benar menyelesaikan apa pun. Sebenarnya, ini mungkin hanya meningkatkan overhead pemrosesan dengan menghasilkan pengecualian. Jika ada yang tahu perbedaan yang membuatnya layak dilakukan, silakan posting!

Dengan menggunakan situs kami, Anda mengakui telah membaca dan memahami Kebijakan Cookie dan Kebijakan Privasi kami.
Licensed under cc by-sa 3.0 with attribution required.