Nesneyi T'ye yayınla


91

XmlReaderNET'teki sınıfla bir XML dosyasını ayrıştırıyorum ve farklı öznitelikleri genel olarak okumak için genel bir ayrıştırma işlevi yazmanın akıllıca olacağını düşündüm. Aşağıdaki işlevi buldum:

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

Fark ettiğim gibi, bu tamamen planladığım gibi çalışmıyor; intveya gibi ilkel türlerde bir hata atar double, çünkü bir çevrim stringa'dan sayısal bir türe dönüşemez. İşlevimin değiştirilmiş biçimde üstün gelmesinin bir yolu var mı?

Yanıtlar:


209

İlk önce, döküm yapılıp yapılamayacağını kontrol edin.

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

1
Convert.ChangeType ile satırı çeşitli farklı kültürel yapılandırmalar üzerinde çalışmasını sağlamak için 'return (T) Convert.ChangeType (readData, typeof (T), System.Globalization.CultureInfo.InstalledUICulture.NumberFormat) olarak değiştirdim.
Kasper Holdum

2
Bu doğru cevap. Ama deneme / yakalama işleminin burada tamamen gereksiz olduğunu iddia edebilirim. Özellikle sessiz istisna düşünüldüğünde. Sanırım if (readData is T) {...} kısmı yeterli bir girişim.
pim

Dönüştürmeden önce readDate'in boş olup olmadığını kontrol edebilirsiniz. Öyleyse varsayılana dönün (T).
Manuel Koch

"Nesne, IConvertible uygulamalı" mesajı alıyorum.
Casey Crookston

19

Convert.ChangeType'ı denediniz mi?

Yöntem her zaman bir dize döndürürse, ki bunu garip buluyorum, ancak bu konunun dışında, o zaman belki de bu değiştirilmiş kod istediğiniz şeyi yapacaktır:

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

Başlangıçta Convert.ChangeType'a bir göz attım, ancak bazı garip nedenlerden dolayı bu işlem için yararlı olmadığına karar verdim. Siz ve Bob da aynı cevabı verdiniz ve cevaplarınız arasında bir karışım oluşturmaya karar verdim, bu yüzden try ifadelerini kullanmaktan kaçındım ama yine de mümkün olduğunda 'return (T) readData' kullandım.
Kasper Holdum

11

Deneyin

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

3

Türün referans türü olmasını zorunlu kılabilirsiniz:

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

Ve sonra değer türleri ve TryParse kullanan başka bir tane yapın ...

 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

Aslında, buradaki sorun ReadContentAsObject'in kullanılmasıdır. Ne yazık ki bu yöntem beklentilerini karşılamıyor; değer için en uygun türü algılaması gerekirken, gerçekte ne olursa olsun bir dizi döndürür (bu, Reflektör kullanılarak doğrulanabilir).

Ancak, sizin özel durumunuzda, çevirmek istediğiniz türü zaten biliyorsunuz, bu nedenle yanlış yöntemi kullandığınızı söyleyebilirim.

Bunun yerine ReadContentAs'ı kullanmayı deneyin, tam da ihtiyacınız olan şey bu.

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

Oldukça kompakt ve zarif görünüyor. Bununla birlikte, kabul edilen cevaptaki çözüm, bir IFormatProvider'ı kabul ettiği için birden çok farklı kültürle uyumlu olan ChangeType'ı kullanır. Bu proje için bir gereklilik olduğu için o çözüme devam edeceğim.
Kasper Holdum

2

Muhtemelen parametre olarak dizeden T'ye dönüşecek bir temsilci aktarabilirsiniz.


1

Bir 'sınıf' kısıtlaması ekleyin (veya daha ayrıntılı, örneğin bir temel sınıf veya beklenilen T nesnelerinizin arayüzü gibi):

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

veya where T : IMyInterfaceveya where T : new(), vb.


1

Aslında, yanıtlar ilginç bir soruyu ortaya çıkarır; bu, hata durumunda işlevinizin yapmasını istediğiniz şeydir.

Belki de bunu T'ye okumaya çalışan, ancak yapılamazsa yanlış döndüren bir TryParse yöntemi biçiminde inşa etmek daha mantıklı olur?

    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: Şimdi düşünüyorum da, convert.changetype testini gerçekten yapmam gerekiyor mu? as hattı zaten bunu yapmaya çalışmıyor mu? Bu ek değişiklik türü çağrısının aslında bir şey başaracağından emin değilim. Aslında, istisna oluşturarak işlem yükünü artırabilir. Biri bunu yapmaya değer kılan bir fark biliyorsa, lütfen gönderin!

Sitemizi kullandığınızda şunları okuyup anladığınızı kabul etmiş olursunuz: Çerez Politikası ve Gizlilik Politikası.
Licensed under cc by-sa 3.0 with attribution required.