2009-07-09 11 views
17

Dizede ve genel türde geçmek istediğim şu senaryoyu kullanıyorum:TryParse() genel bir sürümünde belirli bir türe nasıl dönüştürebilirim?

public class Worker { 
    public void DoSomeWork<T>(string value) 
     where T : struct, IComparable<T>, IEquatable<T> { ... } 
} 

Bir noktada dize değerini T değerine dönüştürmem gerekiyor. Ancak dize T türüne dönüştürülemezse mantık yürütmem gerektiğinden düz bir dönüştürme yapmak istemiyorum.

Convert.ChangeType() kullanmayı deneyebileceğimi düşünüyordum ama bu, eğer dönüştürmezse bir istisna atar ve DoSomeWork() yöntemini çalıştırmaya/denemeye gerek duymayacak kadar sık ​​çalıştıracağım problemi vardır. dönüştürmenin geçerli olup olmadığını belirlemek için.

Bu beni düşünerek, sayısal tiplerle çalışacağımı biliyorum, bu yüzden T aşağıdakilerden biri olacaktır: int, uint, short, ushort, long, ulong, byte, sbyte, decimal, float, double. Bunu bildiğim için, sayısal türleri kullanacağımı bildiğim gerçeğiyle çalışarak daha hızlı bir çözüm bulmak mümkün olabileceğini düşündüm (T, bir istisna atarım sayısal bir tür değilse). Xzx95

public class NumericWorker { 
    public void DoSomeWork<T>(string value) 
     where T : struct, IComparable<T>, IEquatable<T> 
    { 
     ParseDelegate<T> tryConverter = 
      SafeConvert.RetreiveNumericTryParseDelegate<T>(); 
     ... 
    } 
} 


public class SafeConvert 
{ 
    public delegate bool ParseDelegate<T>(string value, out T result); 

    public static ParseDelegate<T> RetreiveNumericTryParseDelegate<T>() 
     where T : struct, IComparable<T>, IEquatable<T> 
    { 
     ParseDelegate<T> tryParseDelegate = null; 

     if (typeof(T) == typeof(int)) 
     { 
      tryParseDelegate = (string v, out T t) => 
       { 
       int typedValue; 
       bool result = int.TryParse(v, out typedValue); 
       t = result ? (T)typedValue : default(T); 
       //(T)Convert.ChangeType(typedValue, typeof(T)) : default(T); 
       return result; 
       }; 
     } 
     else if (typeof(T) == typeof(uint)) { ... } 
     else if (typeof(T) == typeof(short)) { ... } 
     else if (typeof(T) == typeof(ushort)) { ... } 
     else if (typeof(T) == typeof(long)) { ... } 
     else if (typeof(T) == typeof(ulong)) { ... } 
     else if (typeof(T) == typeof(byte)) { ... } 
     else if (typeof(T) == typeof(sbyte)) { ... } 
     else if (typeof(T) == typeof(decimal)) { ... } 
     else if (typeof(T) == typeof(float)) { ... } 
     else if (typeof(T) == typeof(double)) { ... } 

     return tryParseDelegate; 
    } 
} 

Ancak t = result ? (T)typedValue : default(T);typedValue için T'e neden olarak sorunlara neden olan t = result ? (T)typedValue : default(T); yazamadığım problemler var ve şu ana kadar etrafta dolaşabildiğim tek yol (T)Convert.ChangeType(typedValue, typeof(T)) yazıyor. Ama eğer bunu yaparsam başka bir dönüşüm yapıyorum.

Bu yüzden, bu sorunu nasıl çözebileceğimi (ChangeType()'yi yapmayı düşünüyorsanız bir sorun olduğunu düşünüyorsanız) veya daha önce hiç düşünmediğim daha iyi bir çözüm olup olmadığını bilip bilmediğimi merak ediyordum.

cevap

37

t = sonuç? (T) typedValue: varsayılan (T);

Dene:

t = result ? (T)(object)typedValue : default(T); 

Evet, jenerik zamanlarda biraz can sıkıcı olabilir.

FWIW, Convert.ChangeType() çevresinde boş dizeler için bir ön denetim yapan a much simpler wrapper kullanın. Bunu kontrol edilmeyen kullanıcı girişi için kullanmıyorsanız, muhtemelen yeterli olacaktır.

+0

ne thats neden ben çünkü/catch changetype kullanmak ve denemek istemiyordu bu yüzden :(... kullanıcı girişini doğruluyor için kullanıyorum kullanıcının ne yazacağını bilen… –

+0

Heh, tamam :-) FWIW, 'ChangeType() 'tarafından atılan özel durumların verimlilik açısından çok fazla endişelenmem. stil sonra çözümünüz iyi görünüyor. Muhtemelen API'yi tek bir genel yönteme kadar kaynatıp, dışarıdan 'out' parametresi tarafından gizli olarak belirtilebileceğini unutmayın. – Shog9

+0

Her şey basitlik için, "T parametresi tarafından örtülü olarak belirtilir" ile ne demek istiyorsunuz? –

12

Verilen bu: Çift int, uint kısa, USHORT, uzun, ulong, byte, SByte, ondalık, float:

dolayısıyla T aşağıdakilerden herhangi olacaktır.

Sadece Convert.ChangeType'ı kullanmayı ve bunun için endişelenmenizi öneriyorum. Bir istisna elde edeceğiniz tek zaman, dizgenin yanlış biçimlendirilmesidir, bu durumda varsayılanı (T) döndürebilirsiniz.

yani:

try 
{ 
    result = Convert.ChangeType(value, typeof(T)); 
} 
catch 
{ 
    result = default(T); 
} 
+0

@ Shog9 verdiğim yorumda da belirtildiği gibi, problemi kullanıyorum çünkü kullanıcı girdisini doğrulamak için kullanıyorum. metodun çalıştığı her iki saniyede meydana gelebileceğini bildiğiniz bir istisna meydana getiren afaik pahalı bir işlemdir, bu yüzden neden delegeleri kullanmaya çalışıyordum. Kullanmaya çalıştığım temsilci yaklaşımını önerdiğiniz sürümle devam etmeye devam edeceğini söyleyebilir misiniz? –

+0

Şahsen, bunu hala kullanırdım. İstisnaların üstesinden gelmek, bu durumda endişeleneceğim bir şey değildir, özellikle de kullanıcı girdisi için, bunu sıkı bir döngüde aramanız gerekmeyecektir (çünkü kullanıcı bu kadar hızlı yazamaz. istisnayı kaldırabilirsiniz). Anahtar ifadenizde, diğer çağrılarla bile çağrıları, vb. Temsilcilerle başa çıkacaksınız ve yukarıda gördüğüm 6 çizgiyle karşılaştırıldığında bakım yapmak korkunç. –

+0

Reed ile aynı fikirdeyim. İstisna, onlarca milisaniye (en kötü) sırasına göre bir şey alabilir; ancak _one_ kullanıcı giriş dizesini doğrularsanız, bu pek fark edilmeyecektir. –

5

ToType burada jenerik parametre olmaktan.Gerekirse, nealable türleri için çalışır. Nullables dahil olmak üzere herhangi bir türe dönüşecek, jenerik dönüştürücü olmak için ana yönteminizi ayıklayabilirsiniz.

ToType result = default(ToType);  

    result = ChangeType<ToType>(typedValue); 


    private T ChangeType<T>(object o) 
{ 
    Type conversionType = Nullable.GetUnderlyingType(typeof(T)) ?? typeof(T); 
    return (T)Convert.ChangeType(o, conversionType); 
} 
+0

Bu yaklaşımı kullanırsam, bu bir try/catch bloğunun gereksinimini nasıl etkiler? –

+0

istisna, TryParse kullandığınız için atılmayacak. TryParse öğesinin sonucu doğruysa, dönüşüm tamamen farklı bir genel parametre geçirmediğiniz sürece çalışır. bu senin ikilemin mi? –

+0

farklı bir genel parametre iletemediğiniz için üzgünüm, eğer tipeof (T) 'de bir if deyiniz varsa .. sorunuzu tam olarak nedir? –

1

Neden sadece yansıma kullanmıyor ve yerleşik TryParse yöntemlerini kullanıyorsunuz? Guid hariç her yerel tür için hemen hemen bir tane.

public static Parser<T> GetParser<T>(T defaultResult) 
    where T : struct 
{ 
    // create parsing method 
    Parser<T> parser = (string value, out T result) => 
    { 
     // look for TryParse(string value,out T result) 
     var parseMethod = 
      typeof(T).GetMethods() 
        .Where(p => p.Name == "TryParse") 
        .Where(p => p.GetParameters().Length == 2) 
        .Single(); 

     // make parameters, leaving second element uninitialized means out/ref parameter 
     object[] parameters = new object[2]; 
     parameters[0] = value; 

     // run parse method 
     bool success = (bool)parseMethod.Invoke(null, parameters); 

     // if successful, set result to output 
     if (!success) 
     { 
      result = (T)parameters[1]; 
     } 
     else 
     { 
      result = defaultResult; 
     } 

     return success; 
    }; 

    return parser; 
} 
+0

Performansı –

+0

nedeniyle yansımadan kaçınmaya çalışıyorum, eğer test ederseniz, 11 saniyede 100.000'in üzerinde ayrıştırma yapabilirsiniz. .net 4'e 14 saniye süren bir yerel ifade ağacı kullanarak karşılaştırdım (ancak .net 4 yayınlandığında mükemmel yansıtmayabilir) –

2

Sen basit bir şey deneyebilirsiniz

public static T ConvertValue<T,U>(U value) where U : IConvertible { 
     return (T)ConvertValue(value, typeof(T)); 
    } 

    public static object ConvertValue(IConvertible value, Type targetType) { 
     return Convert.ChangeType(value, targetType); 
    } 
İlgili konular