Genel TryParse


196

Bir dize verilen tür olup olmadığını kontrol etmek için 'TryParse' kullanan genel bir uzantı oluşturmaya çalışıyorum:

public static bool Is<T>(this string input)
{
    T notUsed;
    return T.TryParse(input, out notUsed);
}

'TryParse' sembolünü çözemediği için bu derlenmeyecektir

Anladığım kadarıyla, 'TryParse' herhangi bir arayüzün parçası değil.

Bunu yapmak mümkün mü?

Güncelleme:

Aşağıdaki cevapları kullanarak:

public static bool Is<T>(this string input)
{
    try
    {
        TypeDescriptor.GetConverter(typeof(T)).ConvertFromString(input);
    }
    catch
    {
        return false;
    }

    return true;
}

Oldukça iyi çalışıyor ama istisnaları bu şekilde kullanmak benim için doğru gelmiyor.

Update2:

Jenerik kullanmak yerine tip geçmek üzere değiştirildi:

public static bool Is(this string input, Type targetType)
{
    try
    {
        TypeDescriptor.GetConverter(targetType).ConvertFromString(input);
        return true;
    }
    catch
    {
        return false;
    }
}

1
Bence bu genel durumda sadece istisna çamur ile uğraşmak zorunda kalacak. ints veya double gibi şeyleri kontrol etmek için vakalar ekleyebilir ve daha sonra belirli TryParse yöntemlerini kullanabilirsiniz, ancak diğer türleri yakalamak için yine de buna düşmeniz gerekecektir.
luke

1
Jenerik kullanımı gereksizdir. Type olarak parametre olarak girmeniz yeterlidir. public static bool Is (bu dize girdisi, Type targetType). Bu şekilde çağırmak biraz daha güzel görünüyor: x.Is (typeof (int)) -VS- x.Is <int> ()
mikesigs

2
Dönüştürücüde sorun olup olmadığını kontrol etmeniz için dönüştürücüde bir IsValid yöntemi vardır. Aşağıdaki yöntemi kullandım ve iyi çalışıyor gibi görünüyor. protected Boolean TryParse<T>(Object value, out T result) { result = default(T); var convertor = TypeDescriptor.GetConverter(typeof(T)); if (convertor == null || !convertor.IsValid(value)) { return false; } result = (T)convertor.ConvertFrom(value); return true; }
CastroXXL

@CastroXXL Bu soruya ilgi gösterdiğiniz için teşekkür ederiz, ancak yönteminiz nesne türleri için yararlı olsa da (ancak, dize değerinin bir nesne yerine belirli bir türde olup olmadığını kontrol etmek istediğim için yönteminiz işe yaramayacaktır.) istisnaları yakalamak için ConvertFrom(value)yöntemi bir try-catchblokta sarmak zorunda
Piers Myers

2
Kodunuzda ilk kullanımının atılabileceğinden (targetType == null) olup olmadığını kontrol etmelisiniz, ancak bu istisna yakalamanız tarafından yutulabilir.
Nick Strupat

Yanıtlar:


183

TypeDescriptor sınıfını kullanmalısınız :

public static T Convert<T>(this string input)
{
    try
    {
        var converter = TypeDescriptor.GetConverter(typeof(T));
        if(converter != null)
        {
            // Cast ConvertFromString(string text) : object to (T)
            return (T)converter.ConvertFromString(input);
        }
        return default(T);
    }
    catch (NotSupportedException)
    {
        return default(T);
    }
}

3
Yeniden dirildiğim için üzgünüm, GetConverter null döndürüyor mu? Bence, o zaman muhtemelen sessizce başarısız olmak ve başka bir şey döndürmek yerine bir istisna atılmalıdır. Kendi sınıfımda denediğimde (bir tip dönüştürücü tanımlamamıştım), GetConverter'dan bir dönüştürücü aldım, ancak ConvertFromString bir NotSupportedException oluşturdu.
user420667

3
@ user420667, dizeden dönüştürmeye çalışmadan önce CanConvertFrom (typeof (string)) sonucunu kontrol etmeniz gerektiğine inanıyorum. TypeConverter, dizeden dönüştürmeyi desteklemeyebilir.
Reuben Bond

3
Ekleyebilir if (typeof (T) .IsEnum) {return (T) Enum.Parse (typeof (T) giriş); } [tüm Enum türleri için oldukça genel bir kısayol olarak] Dönüştürücü almadan önce. Sanırım Enum türlerini daha karmaşık türlerin aksine ne sıklıkta yapacağınıza bağlı.
Jesse Chisholm

10
Bu yanıt olarak işaretlenebilir ve daha bunun istenenle uygulamıyor zaman upvoted neden anlamıyorum: jenerik deneyin Ayrıştırma. TryParse yöntemlerinin temel amacı, ayrıştırmayı gerçekleştirmeye çalışırken istisnalar atmamaları ve ayrıştırma başarısız olduğunda ve bu çözüm bunu sağlayamadığında performans üzerinde çok daha düşük etkiye sahip olmalarıdır.
Florin Dumitrescu

2
Bu bir sorun, eğer T bir int ise ve girdi int.MaxValue'dan büyükse, iç istisna olarak System.OverFlowException ile System.Exception atar. Bu nedenle, bir OverflowException bekliyorsanız, atılan Exception'ı sorgulamadığınız sürece bunu elde edemezsiniz. Bunun nedeni, ConvertFromString öğesinin bir OverflowException özel durumu oluşturması ve ardından T'ye aktarmanın bir System.Exception özel durumu oluşturmasıdır.
Trevor

78

Ayrıca son zamanlarda genel bir TryParse gerekli. İşte ben geldim;

public static T? TryParse<T>(string value, TryParseHandler<T> handler) where T : struct
{
    if (String.IsNullOrEmpty(value))
        return null;
    T result;
    if (handler(value, out result))
        return result;
    Trace.TraceWarning("Invalid value '{0}'", value);
    return null;
}

public delegate bool TryParseHandler<T>(string value, out T result);

O zaman basitçe bu şekilde arama meselesi:

var value = TryParse<int>("123", int.TryParse);
var value2 = TryParse<decimal>("123.123", decimal.TryParse);

3
Aylar sonra bu yazıyla tekrar karşılaştım ve tekrar kullanırken yöntemin Tişleyiciden çıkarım yapamayacağını fark ettim ve onu Tne zaman çağırdığımızı açıkça belirtmeliyiz . Merak ediyorum, neden çıkarımda bulunamıyorum T?
Nick Strupat

26
Neden bu işlevi kullanmak istersiniz? Değeri ayrıştırmak için hangi işlevi çağıracağınızı biliyorsanız, neden yalnızca doğrudan çağırmıyorsunuz? Doğru giriş türünü zaten biliyor ve jeneriklere gerek yok.Bu çözüm TryParseHandler olmayan türler için işe yaramaz.
xxbbcc

2
@xxbbcc: TryParse, ayrıştırmanın başarılı olup olmadığını gösteren bir boole döndürdüğü için bu işlevi kullanmak istiyorum. Ayrıştırdığınız değeri bir çıktı parametresi aracılığıyla döndürür. Bazen sadece SomeMethod(TryParse<int>(DollarTextbox.Text, int.TryParse))sonucu yakalamak için bir çıktı değişkeni yaratmadan böyle bir şey yapmak istiyorum int.TryParse. Bununla birlikte, Nick'in işlevin türünü çıkarması hakkındaki düşüncesine katılıyorum.
Walter Stabosz

1
Çok verimli bir yöntem. Şiddetle tavsiye edilir.
Vladimir Kocjancic

3
Üçüncü bir parametre olarak varsayılan bir değer tavsiye ederim. Bu T'nin çıkartılamadığı sorunu çözer. Ayrıca, dize değeri geçersizse hangi değerin istendiğine karar vermenizi sağlar. Örneğin, -1 geçersiz anlamına gelebilir. public static T TryParse <T> (dize değeri, TryParseHandler <T> işleyicisi, T defaultValue)
Rhyous

33

Akış kontrolü için deneme / yakalama yöntemlerini kullanmak korkunç bir politikadır. Bir istisna atmak, çalışma zamanı istisna etrafında çalışırken performans gecikmelerine neden olur. Bunun yerine dönüştürmeden önce verileri doğrulayın.

var attemptedValue = "asdfasdsd";
var type = typeof(int);
var converter = TypeDescriptor.GetConverter(type);
if (converter != null &&  converter.IsValid(attemptedValue))
    return converter.ConvertFromString(attemptedValue);
else
    return Activator.CreateInstance(type);

2
Koddan converter != nullkaldırılabilir böylece her zaman doğru bir Resharper bildirimi alıyorum .
ErikE

5
@ErikE Bu ReSharper uyarılarına her zaman güvenmiyorum. Genellikle çalışma zamanında ne olduğunu göremezler.
ProfK


@danio Deneyimlerimi genel olarak böyle R # uyarılarıyla paylaşıyordum. Kesinlikle bu durumda yanlış olduğunu ima etmedim.
ProfK

14

TryParse kullanmaya ayarlıysanız, yansımayı kullanabilir ve şu şekilde yapabilirsiniz:

public static bool Is<T>(this string input)
{
    var type = typeof (T);
    var temp = default(T);
    var method = type.GetMethod(
        "TryParse",
        new[]
            {
                typeof (string),
                Type.GetType(string.Format("{0}&", type.FullName))
            });
    return (bool) method.Invoke(null, new object[] {input, temp});
}

Bu çok havalı ve yine de sevmediğim istisnalardan kurtuluyor. Yine de biraz kıvrık.
Piers Myers

6
Güzel bir çözüm, ancak yansımayı içeren herhangi bir cevap (özellikle bir iç döngüden kolayca çağrılabilen bir yardımcı yöntemde) performans hakkında bir feragatnameye ihtiyaç duyar. Bkz. Stackoverflow.com/questions/25458/how-costly-is-net-reflection
Patrick M

İç çekmek. Yani seçenekler (1) kod akış kontrolü için istisnalar kullanır, (2) hız maliyetleri ile yansıma kullanır. @PiersMyers ile hemfikirim - hiçbir seçim ideal değil. İkisi de iyi iş çıkarıyor. :)
Jesse Chisholm

Sana yerini alabilir mi Type.GetType(string.Format(...))sahip type.MakeByRefType().
Drew Noakes

3
yöntemin çağrı başına bir kez değil, tür başına yalnızca bir kez yansıtılması gerekir. bunu statik üye değişkenli genel bir sınıf yaparsanız, ilk yansımanın çıktısını yeniden kullanabilirsiniz.
Andrew Hill

7

Bu, her genel tür için statik bir yapıcı kullanır, bu nedenle yalnızca belirli bir türde ilk kez çağırdığınızda pahalı işi yapması gerekir. Sistem ad alanındaki TryParse yöntemlerine sahip tüm türleri işler. Ayrıca numaralandırmalar dışında, bunların her birinin nullable sürümleriyle (yapılardır) çalışır.

    public static bool TryParse<t>(this string Value, out t result)
    {
        return TryParser<t>.TryParse(Value.SafeTrim(), out result);
    }
    private delegate bool TryParseDelegate<t>(string value, out t result);
    private static class TryParser<T>
    {
        private static TryParseDelegate<T> parser;
        // Static constructor:
        static TryParser()
        {
            Type t = typeof(T);
            if (t.IsEnum)
                AssignClass<T>(GetEnumTryParse<T>());
            else if (t == typeof(bool) || t == typeof(bool?))
                AssignStruct<bool>(bool.TryParse);
            else if (t == typeof(byte) || t == typeof(byte?))
                AssignStruct<byte>(byte.TryParse);
            else if (t == typeof(short) || t == typeof(short?))
                AssignStruct<short>(short.TryParse);
            else if (t == typeof(char) || t == typeof(char?))
                AssignStruct<char>(char.TryParse);
            else if (t == typeof(int) || t == typeof(int?))
                AssignStruct<int>(int.TryParse);
            else if (t == typeof(long) || t == typeof(long?))
                AssignStruct<long>(long.TryParse);
            else if (t == typeof(sbyte) || t == typeof(sbyte?))
                AssignStruct<sbyte>(sbyte.TryParse);
            else if (t == typeof(ushort) || t == typeof(ushort?))
                AssignStruct<ushort>(ushort.TryParse);
            else if (t == typeof(uint) || t == typeof(uint?))
                AssignStruct<uint>(uint.TryParse);
            else if (t == typeof(ulong) || t == typeof(ulong?))
                AssignStruct<ulong>(ulong.TryParse);
            else if (t == typeof(decimal) || t == typeof(decimal?))
                AssignStruct<decimal>(decimal.TryParse);
            else if (t == typeof(float) || t == typeof(float?))
                AssignStruct<float>(float.TryParse);
            else if (t == typeof(double) || t == typeof(double?))
                AssignStruct<double>(double.TryParse);
            else if (t == typeof(DateTime) || t == typeof(DateTime?))
                AssignStruct<DateTime>(DateTime.TryParse);
            else if (t == typeof(TimeSpan) || t == typeof(TimeSpan?))
                AssignStruct<TimeSpan>(TimeSpan.TryParse);
            else if (t == typeof(Guid) || t == typeof(Guid?))
                AssignStruct<Guid>(Guid.TryParse);
            else if (t == typeof(Version))
                AssignClass<Version>(Version.TryParse);
        }
        private static void AssignStruct<t>(TryParseDelegate<t> del)
            where t: struct
        {
            TryParser<t>.parser = del;
            if (typeof(t).IsGenericType
                && typeof(t).GetGenericTypeDefinition() == typeof(Nullable<>))
            {
                return;
            }
            AssignClass<t?>(TryParseNullable<t>);
        }
        private static void AssignClass<t>(TryParseDelegate<t> del)
        {
            TryParser<t>.parser = del;
        }
        public static bool TryParse(string Value, out T Result)
        {
            if (parser == null)
            {
                Result = default(T);
                return false;
            }
            return parser(Value, out Result);
        }
    }

    private static bool TryParseEnum<t>(this string Value, out t result)
    {
        try
        {
            object temp = Enum.Parse(typeof(t), Value, true);
            if (temp is t)
            {
                result = (t)temp;
                return true;
            }
        }
        catch
        {
        }
        result = default(t);
        return false;
    }
    private static MethodInfo EnumTryParseMethod;
    private static TryParseDelegate<t> GetEnumTryParse<t>()
    {
        Type type = typeof(t);

        if (EnumTryParseMethod == null)
        {
            var methods = typeof(Enum).GetMethods(
                BindingFlags.Public | BindingFlags.Static);
            foreach (var method in methods)
                if (method.Name == "TryParse"
                    && method.IsGenericMethodDefinition
                    && method.GetParameters().Length == 2
                    && method.GetParameters()[0].ParameterType == typeof(string))
                {
                    EnumTryParseMethod = method;
                    break;
                }
        }
        var result = Delegate.CreateDelegate(
            typeof(TryParseDelegate<t>),
            EnumTryParseMethod.MakeGenericMethod(type), false)
            as TryParseDelegate<t>;
        if (result == null)
            return TryParseEnum<t>;
        else
            return result;
    }

    private static bool TryParseNullable<t>(string Value, out t? Result)
        where t: struct
    {
        t temp;
        if (TryParser<t>.TryParse(Value, out temp))
        {
            Result = temp;
            return true;
        }
        else
        {
            Result = null;
            return false;
        }
    }

6

Böyle bir şeye ne dersin?

http://madskristensen.net/post/Universal-data-type-checker.aspx ( Arşiv )

/// <summary> 
/// Checks the specified value to see if it can be 
/// converted into the specified type. 
/// <remarks> 
/// The method supports all the primitive types of the CLR 
/// such as int, boolean, double, guid etc. as well as other 
/// simple types like Color and Unit and custom enum types. 
/// </remarks> 
/// </summary> 
/// <param name="value">The value to check.</param> 
/// <param name="type">The type that the value will be checked against.</param> 
/// <returns>True if the value can convert to the given type, otherwise false. </returns> 
public static bool CanConvert(string value, Type type) 
{ 
    if (string.IsNullOrEmpty(value) || type == null) return false;
    System.ComponentModel.TypeConverter conv = System.ComponentModel.TypeDescriptor.GetConverter(type);
    if (conv.CanConvertFrom(typeof(string)))
    { 
        try 
        {
            conv.ConvertFrom(value); 
            return true;
        } 
        catch 
        {
        } 
     } 
     return false;
  }

Bu oldukça kolay bir şekilde genel bir yönteme dönüştürülebilir.

 public static bool Is<T>(this string value)
 {
    if (string.IsNullOrEmpty(value)) return false;
    var conv = System.ComponentModel.TypeDescriptor.GetConverter(typeof(T));

    if (conv.CanConvertFrom(typeof(string)))
    { 
        try 
        {
            conv.ConvertFrom(value); 
            return true;
        } 
        catch 
        {
        } 
     } 
     return false;
}

Try bloğundan true döndürmeniz veya catch bloğundan false döndürmeniz önemli midir? Sanmıyorum, ama yine de bu şekilde istisnaları kullanmanın bana yanlış geldiğini düşünüyorum ...
Piers Myers

3
Yakalama bloğundan dönüp dönmemeniz önemli değil, bu aynı. Btw. Genel bir yakalama yan tümcesi olması kötüdür catch { }. Ancak, bu durumda alternatif yoktur, çünkü bir dönüştürme hatası durumunda .NET temel sınıfı BaseNumberConverteratar Exception. Bu çok talihsiz. Aslında bu taban tipi atıldığı için hala birkaç yer var. Umarım Microsoft bunları çerçevenin gelecekteki bir sürümünde düzeltir.
Steven

Teşekkürler Steven, Daha iyi söyleyemezdim.
Bob

Dönüşümün sonucu kullanılmaz: kod gereksizdir.
BillW

4

Genel tiplerde yapamazsınız.

Yapabileceğiniz şey ITryParsable arabirimi oluşturmak ve bu arabirimi uygulayan özel türler için kullanmaktır.

Sanırım bunu intve gibi temel türlerle kullanmayı planlıyorsunuz DateTime. Yeni arabirimler uygulamak için bu türleri değiştiremezsiniz.


1
Acaba .net 4 dinamik anahtar kelime kullanarak işe yarayacak mı?
Pierre-Alain Vigeant

@Pierre: Statik yazımda çalışmadığından, C # 'da dynamicanahtar kelime ile varsayılan olarak çalışmaz. Bunu başarabilecek kendi dinamik nesnenizi oluşturabilirsiniz, ancak bu varsayılan değildir.
Steven

4

Burada Charlie Brown tarafından yayınlanan çözümden esinlenerek, isteğe bağlı olarak ayrıştırılan değeri çıkaran yansıma kullanarak genel bir TryParse oluşturdum:

/// <summary>
/// Tries to convert the specified string representation of a logical value to
/// its type T equivalent. A return value indicates whether the conversion
/// succeeded or failed.
/// </summary>
/// <typeparam name="T">The type to try and convert to.</typeparam>
/// <param name="value">A string containing the value to try and convert.</param>
/// <param name="result">If the conversion was successful, the converted value of type T.</param>
/// <returns>If value was converted successfully, true; otherwise false.</returns>
public static bool TryParse<T>(string value, out T result) where T : struct {
    var tryParseMethod = typeof(T).GetMethod("TryParse", BindingFlags.Static | BindingFlags.Public, null, new [] { typeof(string), typeof(T).MakeByRefType() }, null);
    var parameters = new object[] { value, null };

    var retVal = (bool)tryParseMethod.Invoke(null, parameters);

    result = (T)parameters[1];
    return retVal;
}

/// <summary>
/// Tries to convert the specified string representation of a logical value to
/// its type T equivalent. A return value indicates whether the conversion
/// succeeded or failed.
/// </summary>
/// <typeparam name="T">The type to try and convert to.</typeparam>
/// <param name="value">A string containing the value to try and convert.</param>
/// <returns>If value was converted successfully, true; otherwise false.</returns>
public static bool TryParse<T>(string value) where T : struct {
    T throwaway;
    var retVal = TryParse(value, out throwaway);
    return retVal;
}

Bu şekilde çağrılabilir:

string input = "123";
decimal myDecimal;

bool myIntSuccess = TryParse<int>(input);
bool myDecimalSuccess = TryParse<decimal>(input, out myDecimal);

Güncelleme:
Ayrıca YotaXP'nin gerçekten sevdiğim çözümü sayesinde, uzantı yöntemlerini kullanmayan, ancak hala bir singletona sahip, yansıma yapma ihtiyacını en aza indiren bir sürüm oluşturdum:

/// <summary>
/// Provides some extra parsing functionality for value types.
/// </summary>
/// <typeparam name="T">The value type T to operate on.</typeparam>
public static class TryParseHelper<T> where T : struct {
    private delegate bool TryParseFunc(string str, out T result);

    private static TryParseFunc tryParseFuncCached;

    private static TryParseFunc tryParseCached {
        get {
            return tryParseFuncCached ?? (tryParseFuncCached = Delegate.CreateDelegate(typeof(TryParseFunc), typeof(T), "TryParse") as TryParseFunc);
        }
    }

    /// <summary>
    /// Tries to convert the specified string representation of a logical value to
    /// its type T equivalent. A return value indicates whether the conversion
    /// succeeded or failed.
    /// </summary>
    /// <param name="value">A string containing the value to try and convert.</param>
    /// <param name="result">If the conversion was successful, the converted value of type T.</param>
    /// <returns>If value was converted successfully, true; otherwise false.</returns>
    public static bool TryParse(string value, out T result) {
        return tryParseCached(value, out result);
    }

    /// <summary>
    /// Tries to convert the specified string representation of a logical value to
    /// its type T equivalent. A return value indicates whether the conversion
    /// succeeded or failed.
    /// </summary>
    /// <param name="value">A string containing the value to try and convert.</param>
    /// <returns>If value was converted successfully, true; otherwise false.</returns>
    public static bool TryParse(string value) {
        T throwaway;
        return TryParse(value, out throwaway);
    }
}

Buna şöyle deyin:

string input = "987";
decimal myDecimal;

bool myIntSuccess = TryParseHelper<int>.TryParse(input);
bool myDecimalSuccess = TryParseHelper<decimal>.TryParse(input, out myDecimal);

3

Partiye biraz geç kaldım, ama işte geldiklerim. İstisna yok, tek seferlik (tip başına) yansıma.

public static class Extensions {
    public static T? ParseAs<T>(this string str) where T : struct {
        T val;
        return GenericHelper<T>.TryParse(str, out val) ? val : default(T?);
    }
    public static T ParseAs<T>(this string str, T defaultVal) {
        T val;
        return GenericHelper<T>.TryParse(str, out val) ? val : defaultVal;
    }

    private static class GenericHelper<T> {
        public delegate bool TryParseFunc(string str, out T result);

        private static TryParseFunc tryParse;
        public static TryParseFunc TryParse {
            get {
                if (tryParse == null)
                    tryParse = Delegate.CreateDelegate(
                        typeof(TryParseFunc), typeof(T), "TryParse") as TryParseFunc;
                return tryParse;
            }
        }
    }
}

Ek sınıf gereklidir, çünkü genel sınıflar içinde uzatma yöntemlerine izin verilmez. Bu, aşağıda gösterildiği gibi basit kullanıma izin verir ve yalnızca bir tür ilk kullanıldığında yansımayı vurur.

"5643".ParseAs<int>()

3

İşte başka bir seçenek.

İstediğiniz sayıda TryParseişleyiciyi kaydetmeyi kolaylaştıran bir sınıf yazdım . Bunu yapmama izin veriyor:

var tp = new TryParser();

tp.Register<int>(int.TryParse);
tp.Register<decimal>(decimal.TryParse);
tp.Register<double>(double.TryParse);

int x;
if (tp.TryParse("42", out x))
{
    Console.WriteLine(x);
};

Ben olsun 42konsola basılmış.

Sınıf:

public class TryParser
{
    public delegate bool TryParseDelegate<T>(string s, out T result);

    private Dictionary<Type, Delegate> _tryParsers = new Dictionary<Type, Delegate>();

    public void Register<T>(TryParseDelegate<T> d)
    {
        _tryParsers[typeof(T)] = d;
    }

    public bool Deregister<T>()
    {
        return _tryParsers.Remove(typeof(T));
    }

    public bool TryParse<T>(string s, out T result)
    {
        if (!_tryParsers.ContainsKey(typeof(T)))
        {
            throw new ArgumentException("Does not contain parser for " + typeof(T).FullName + ".");
        }
        var d = (TryParseDelegate<T>)_tryParsers[typeof(T)];
        return d(s, out result);
    }
}

Bunu beğendim, ama jenerik olmadan nasıl yapardınız? Tabii ki yansıma olgusu kullanın.
Sinaestetik

Bazı yansıma korsanlığı yapan aşırı yüklenmiş bir yöntem ekledim. Bunu ele almanın daha zarif bir yolu varsa, ben tüm gözlerim lol gist.github.com/dasjestyr/90d8ef4dea179a6e08ddd85e0dacbc94
Sinaesthetic

2

Neredeyse bu şeyi yapmak istediğimde, yansıma verildiğinde onu zor yoldan uygulamak zorunda kaldım. Verilen T, üzerinde düşünün ve bulursanız typeof(T)bir TryParseveya Parseyöntemi arayın .


Bunu önerecektim.
Steven Evers

2

Bu benim denemem. Bunu bir "egzersiz" olarak yaptım. Mevcut " Convert.ToX () " -ones vb kullanmak için benzer yapmaya çalıştım . Ama bu bir uzantısı yöntemidir:

    public static bool TryParse<T>(this String str, out T parsedValue)
    {
        try
        {
            parsedValue = (T)Convert.ChangeType(str, typeof(T));
            return true;
        }

        catch { parsedValue = default(T); return false; }
    }

Buna kıyasla ana dezavantajı TypeConverter.ConvertFrom(), kaynak sınıfın tür dönüşümünü sağlaması gerektiğidir, bu da genellikle özel türlere dönüştürmeyi destekleyemeyeceğiniz anlamına gelir.
Ian Goldby

1

Dediğiniz gibi TryParsebir arayüzün parçası değil. Ayrıca, gerçekte olduğu staticve staticişlevler yapılamadığı için herhangi bir temel sınıfın üyesi değildir virtual. Yani, derleyicinin Taslında adlı bir üyesi olduğundan emin olmanın bir yolu yoktur TryParse, bu yüzden bu işe yaramaz.

@Mark'ın dediği gibi, kendi arayüzünüzü oluşturabilir ve özel türler kullanabilirsiniz, ancak yerleşik türler için şansınız kalmadı.


1
public static class Primitive
{
    public static DateTime? TryParseExact(string text, string format, IFormatProvider formatProvider = null, DateTimeStyles? style = null)
    {
        DateTime result;
        if (DateTime.TryParseExact(text, format, formatProvider, style ?? DateTimeStyles.None, out result))
            return result;
        return null;
    }

    public static TResult? TryParse<TResult>(string text) where TResult : struct
    {
        TResult result;
        if (Delegates<TResult>.TryParse(text, out result))
            return result;
        return null;
    }

    public static bool TryParse<TResult>(string text, out TResult result) => Delegates<TResult>.TryParse(text, out result);

    public static class Delegates<TResult>
    {
        private delegate bool TryParseDelegate(string text, out TResult result);

        private static readonly TryParseDelegate _parser = (TryParseDelegate)Delegate.CreateDelegate(typeof(TryParseDelegate), typeof(TResult), "TryParse");

        public static bool TryParse(string text, out TResult result) => _parser(text, out result);
    }
}

0

Bu bir 'genel kısıtlamalar' meselesidir. Belirli bir arayüze sahip olmadığınız için, önceki cevabın önerilerini takip etmedikçe sıkışıp kalırsınız.

Bununla ilgili belgeler için aşağıdaki bağlantıyı kontrol edin:

http://msdn.microsoft.com/en-us/library/ms379564(VS.80).aspx

Bu kısıtlamaları nasıl kullanacağınızı gösterir ve size daha fazla ipucu vermelidir.


0

Ödünç alındı Http://blogs.msdn.com/b/davidebb/archive/2009/10/23/using-c-dynamic-to-call-static-members.aspx adresinden

Bu başvuruyu izlerken: C # 4.0 dinamik türü ile statik yöntem nasıl çağırılır?

using System;
using System.Collections.Generic;
using System.Dynamic;
using System.Linq;
using System.Reflection;

namespace Utils
{
   public class StaticMembersDynamicWrapper : DynamicObject
   {
      private Type _type;

      public StaticMembersDynamicWrapper(Type type) { _type = type; }

      // Handle static methods
      public override bool TryInvokeMember(InvokeMemberBinder binder, object[] args, out object result)
      {
         var methods = _type
            .GetMethods(BindingFlags.FlattenHierarchy | BindingFlags.Static | BindingFlags.Public)
            .Where(methodInfo => methodInfo.Name == binder.Name);

         var method = methods.FirstOrDefault();
         if (method != null)
         {
            result = method.Invoke(null, args);
            return true;
         }

         result = null;
         return false;
      }
   }

   public static class StaticMembersDynamicWrapperExtensions
   {
      static Dictionary<Type, DynamicObject> cache =
         new Dictionary<Type, DynamicObject>
         {
            {typeof(double), new StaticMembersDynamicWrapper(typeof(double))},
            {typeof(float), new StaticMembersDynamicWrapper(typeof(float))},
            {typeof(uint), new StaticMembersDynamicWrapper(typeof(uint))},
            {typeof(int), new StaticMembersDynamicWrapper(typeof(int))},
            {typeof(sbyte), new StaticMembersDynamicWrapper(typeof(sbyte))}
         };

      /// <summary>
      /// Allows access to static fields, properties, and methods, resolved at run-time.
      /// </summary>
      public static dynamic StaticMembers(this Type type)
      {
         DynamicObject retVal;
         if (!cache.TryGetValue(type, out retVal))
            return new StaticMembersDynamicWrapper(type);

         return retVal;
      }
   }
}

Ve aşağıdaki gibi kullanın:

  public static T? ParseNumeric<T>(this string str, bool throws = true)
     where T : struct
  {
     var statics = typeof(T).StaticMembers();

     if (throws) return statics.Parse(str);

     T retval;
     if (!statics.TryParse(str, out retval)) return null;

     return retval;
  }

0

Böyle bir şey elde etmeyi başardım

    var result = "44".TryParse<int>();

    Console.WriteLine( "type={0}, value={1}, valid={2}",        
    result.Value.GetType(), result.Value, result.IsValid );

İşte kodum

 public static class TryParseGeneric
    {
        //extend int
        public static dynamic TryParse<T>( this string input )
        {    
            dynamic runner = new StaticMembersDynamicWrapper( typeof( T ) );

            T value;
            bool isValid = runner.TryParse( input, out value );
            return new { IsValid = isValid, Value = value };
        }
    }


    public class StaticMembersDynamicWrapper : DynamicObject
    {
        private readonly Type _type;
        public StaticMembersDynamicWrapper( Type type ) { _type = type; }

        // Handle static properties
        public override bool TryGetMember( GetMemberBinder binder, out object result )
        {
            PropertyInfo prop = _type.GetProperty( binder.Name, BindingFlags.FlattenHierarchy | BindingFlags.Static | BindingFlags.Public );
            if ( prop == null )
            {
                result = null;
                return false;
            }

            result = prop.GetValue( null, null );
            return true;
        }

        // Handle static methods
        public override bool TryInvokeMember( InvokeMemberBinder binder, object [] args, out object result )
        {
            var methods = _type
            .GetMethods( BindingFlags.FlattenHierarchy | BindingFlags.Static | BindingFlags.Public ).Where( methodInfo => methodInfo.Name == binder.Name );

            var method = methods.FirstOrDefault();

            if ( method == null )
            {
                result = null;

                return false;
            }

            result = method.Invoke( null, args );

            return true;
        }
    }

StaticMembersDynamicWrapper, David Ebbo'nun makalesinden uyarlanmıştır (bir AmbiguousMatchException oluşturuyordu)


0
public static T Get<T>(string val)
{ 
    return (T) TypeDescriptor.GetConverter(typeof (T)).ConvertFromInvariantString(val);
}

0

İle TypeDescriptorsınıf kullanımı TryParseile ilgili bir tarzda:

public static bool TryParse<T>(this string input, out T parsedValue)
{
    parsedValue = default(T);
    try
    {
        var converter = TypeDescriptor.GetConverter(typeof(T));
        parsedValue = (T)converter.ConvertFromString(input);
        return true;
    }
    catch (NotSupportedException)
    {
        return false;
    }
}

Bu kod soruyu çözebilir, ancak bunun sorunun nasıl ve neden çözüldüğüne dair bir açıklama da dahil olmak üzere , yayınınızın kalitesini artırmaya yardımcı olabilir ve muhtemelen daha fazla oyla sonuçlanır. Sadece şimdi soran kişi için değil, gelecekte okuyucular için soruyu cevapladığınızı unutmayın. Lütfen açıklama eklemek için cevabınızı düzenleyin ve hangi sınırlamaların ve varsayımların geçerli olduğunu belirtin.
çift ​​bip

0

Yukarıdaki bilgileri kullanarak, bunu geliştirdim. Nesneyi doğrudan dönüştürmek mümkündür, aksi takdirde nesneyi bir dizeye dönüştürür ve istenen nesne türü için TryParse yöntemini çağırır.

Her bir yöntem getirme yükünü azaltmak için karşılaşıldığı gibi bir sözlükte yöntemleri önbelleğe.

Nesnenin hedef türüne doğrudan dönüştürülüp dönüştürülemeyeceğini test etmek mümkündür, bu da dize dönüştürme parçasını daha da azaltacaktır. Ama şimdilik dışarıda bırakacağım.

    /// <summary>
    /// Used to store TryParse converter methods
    /// </summary>
    private static readonly Dictionary<Type, MethodInfo> TypeConverters = new Dictionary<Type, MethodInfo>();

    /// <summary>
    /// Attempt to parse the input object to the output type
    /// </summary>
    /// <typeparam name="T">output type</typeparam>
    /// <param name="obj">input object</param>
    /// <param name="result">output result on success, default(T) on failure</param>
    /// <returns>Success</returns>
    public static bool TryParse<T>([CanBeNull] object obj, out T result)
    {
        result = default(T);

        try
        {
            switch (obj)
            {
                // don't waste time on null objects
                case null: return false;

                // if the object is already of type T, just return the value
                case T val:
                    result = val;
                    return true;
            }

            // convert the object into type T via string conversion
            var input = ((obj as string) ?? obj.ToString()).Trim();
            if (string.IsNullOrEmpty(input)) return false;

            var type = typeof (T);
            Debug.WriteLine($"Info: {nameof(TryParse)}<{type.Name}>({obj.GetType().Name}=\"{input}\")");

            if (! TypeConverters.TryGetValue(type, out var method))
            {
                // get the TryParse method for this type
                method = type.GetMethod("TryParse",
                    new[]
                    {
                        typeof (string),
                        Type.GetType($"{type.FullName}&")
                    });

                if (method is null)
                    Debug.WriteLine($"FAILED: Cannot get method for {type.Name}.TryParse()");

                // store it so we don't have to do this again
                TypeConverters.Add(type, method);
            }

            // have to keep a reference to parameters if you want to get the returned ref value
            var parameters = new object[] {input, null};
            if ((bool?) method?.Invoke(null, parameters) == true)
            {
                result = (T) parameters[1];
                return true;
            }                
        }
        catch (Exception ex)
        {
            Debug.WriteLine(ex);
        }

        return false;
    }

Numaralandırmaları desteklemek için başka bir işlev eklemek zorunda kaldım. Enum ayrıştırma "nerede T: struct" özniteliğini gerektirir ve bunun dönüştürülebilir bir şey üzerinde çalışmak istiyorum. (muhtemelen türe dönüştürülebilir bir özellik eklemelidir). Ancak, aşağıdaki önerilerden bazıları daha basit (dolayısıyla daha iyi) görünmektedir.
B Duffy

0

Buraya bir sürü fikir bir araya getirdim ve çok kısa bir çözüm buldum.

Bu, bir dizede bir uzantı yöntemidir

enter code here

Sayısal türlerde TryParse yöntemleriyle aynı ayak izi ile yaptım

    /// <summary>
    /// string.TryParse()
    /// 
    /// This generic extension method will take a string
    ///     make sure it is not null or empty
    ///     make sure it represents some type of number e.g. "123" not "abc"
    ///     It then calls the appropriate converter for the type of T
    /// </summary>
    /// <typeparam name="T">The type of the desired retrunValue e.g. int, float, byte, decimal...</typeparam>
    /// <param name="targetText">The text to be converted</param>
    /// <param name="returnValue">a populated value of the type T or the default(T) value which is likely to be 0</param>
    /// <returns>true if the string was successfully parsed and converted otherwise false</returns>
    /// <example>
    /// float testValue = 0;
    ///  if ( "1234".TryParse<float>( out testValue ) )
    ///  {
    ///      doSomethingGood();
    ///  }
    ///  else
    ///  {
    ///      handleTheBadness();
    ///  }
    /// </example>
    public static bool TryParse<T>(this string targetText, out T returnValue )
    {
        bool returnStatus = false;

        returnValue = default(T);

        //
        // make sure the string is not null or empty and likely a number...
        // call whatever you like here or just leave it out - I would
        // at least make sure the string was not null or empty  
        //
        if ( ValidatedInputAnyWayYouLike(targetText) )
        {

            //
            // try to catch anything that blows up in the conversion process...
            //
            try
            {
                var type = typeof(T);
                var converter = TypeDescriptor.GetConverter(type);

                if (converter != null && converter.IsValid(targetText))
                {
                    returnValue = (T)converter.ConvertFromString(targetText);
                    returnStatus = true;
                }

            }
            catch
            {
                // just swallow the exception and return the default values for failure
            }

        }

        return (returnStatus);

    }

''


şamandıra testi Değer = 0; if ("1234" .TryParse <float> (testValue dışı)) {doSomethingGood (); } else {handleTheBadness (); }
JD Hicks

0

T. DeneParse ... neden?

Böyle bir jenerik TryParsefonksiyona sahip olmanın hiçbir faydasını görmüyorum . Olası çelişkili davranışlarla farklı türler arasında veri ayrıştırmak ve dönüştürmek için çok fazla farklı strateji vardır. Bu işlev bağlamsız bir şekilde hangi stratejiyi seçeceğini nasıl bilebilir?

  • özel TryParse işlevlerine sahip sınıflar çağrılabilir
  • Ayrıştırılmış Ayrıştırma işlevlerine sahip sınıflar try-catch ve bool sonucuyla sarılabilir
  • aşırı yüklenme sınıfları, ayrıştırma işlemlerini nasıl gerçekleştirmelerine izin verirsiniz?
  • tip tanımlayıcılar Convert.ChangeType . Bu API çalışma zamanında özelleştirilebilir. İşleviniz varsayılan davranış gerektiriyor mu veya özelleştirmeye izin veriyor mu?
  • Herhangi bir eşleme çerçevesinin sizin için ayrıştırmaya çalışmasına izin vermeli misiniz?
  • yukarıdakilerdeki çatışmaları nasıl ele alırsınız?

-2

XDocument'ten torunları almak için bir sürüm.

public static T Get<T>(XDocument xml, string descendant, T @default)
{
    try
    {
        var converter = TypeDescriptor.GetConverter(typeof (T));
        if (converter != null)
        {
            return (T) converter.ConvertFromString(xml.Descendants(descendant).Single().Value);
        }
        return @default;
    }
    catch
    {
        return @default;
    }
}
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.