TryParse Umum


196

Saya mencoba membuat ekstensi umum yang menggunakan 'TryParse' untuk memeriksa apakah string adalah tipe yang diberikan:

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

ini tidak dapat dikompilasi karena tidak dapat menyelesaikan simbol 'TryParse'

Seperti yang saya mengerti, 'TryParse' bukan bagian dari antarmuka apa pun.

Apakah ini bisa dilakukan?

Memperbarui:

Menggunakan jawaban di bawah ini saya telah menemukan:

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

    return true;
}

Ini berfungsi cukup baik tetapi saya pikir menggunakan pengecualian dengan cara itu tidak terasa benar bagi saya.

Pembaruan2:

Dimodifikasi untuk lulus jenis daripada menggunakan obat generik:

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

1
Saya pikir dalam kasus umum ini Anda hanya harus berurusan dengan kludge pengecualian. Anda bisa menambahkan case untuk memeriksa hal-hal seperti int atau dobel dan kemudian menggunakan metode TryParse spesifik, tetapi Anda masih harus kembali pada ini untuk menangkap jenis lain.
Lukas

1
Penggunaan generik tidak perlu. Cukup masukkan Jenis sebagai parameter. public static bool Is (input string ini, Ketik targetType). Dengan cara itu memanggilnya terlihat sedikit lebih cantik: x.Is (typeof (int)) -VS- x.Apakah <int> ()
mikesigs

2
Ada metode IsValid pada konverter untuk Anda memeriksa apakah konversi akan memiliki masalah. Saya menggunakan metode di bawah ini dan tampaknya berfungsi dengan baik. 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 Terima kasih telah menunjukkan minat pada pertanyaan ini, namun metode Anda tidak cukup berfungsi karena saya ingin memeriksa apakah nilai string dari jenis tertentu daripada objek, meskipun metode Anda akan berguna untuk jenis objek (tetapi apakah harus membungkus ConvertFrom(value)metode dalam try-catchblok untuk menangkap pengecualian
Piers Myers

2
Anda harus memeriksa apakah (targetType == null) karena penggunaan pertama kali dalam kode Anda mungkin melempar tetapi pengecualian itu akan tertelan oleh tangkapan Anda.
Nick Strupat

Jawaban:


183

Anda harus menggunakan kelas TypeDescriptor :

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
Maaf untuk menghidupkan kembali, tetapi apakah GetConverter mengembalikan nol? Saya pikir jika itu terjadi maka mungkin pengecualian harus dilemparkan alih-alih pada dasarnya gagal dan mengembalikan sesuatu yang lain. Ketika saya mencobanya di kelas saya sendiri (di mana saya tidak mendefinisikan typeconverter), saya mendapat konverter dari GetConverter, tetapi kemudian ConvertFromString melempar NotSupportedException.
user420667

3
@ user420667, saya yakin Anda harus memeriksa hasil CanConvertFrom (typeof (string)) sebelum mencoba untuk mengkonversi dari string. TypeConverter mungkin tidak mendukung konversi dari string.
Reuben Bond

3
Anda dapat menambahkan if (typeof (T) .IsEnum) {return (T) Enum.Parse (typeof (T), input); } [sebagai jalan pintas yang cukup umum untuk semua jenis Enum] sebelum mendapatkan Konverter. Saya kira itu tergantung pada seberapa sering Anda akan melakukan jenis Enum sebagai lawan jenis yang lebih kompleks.
Jesse Chisholm

10
Saya tidak mengerti mengapa ini ditandai sebagai jawaban dan terunggul begitu banyak ketika tidak menerapkan apa yang diminta: Try Parse generik . Tujuan utama metode TryParse adalah mereka tidak membuang pengecualian ketika mencoba melakukan parsing dan memiliki dampak yang jauh lebih rendah pada kinerja ketika parsing gagal dan solusi ini gagal memberikan hal itu.
Florin Dumitrescu

2
Satu masalah w / ini adalah bahwa jika T adalah int dan input lebih besar dari int.MaxValue, itu akan membuang System.Exception w / System.OverFlowException sebagai pengecualian dalam. Jadi jika Anda mengharapkan OverflowException, Anda tidak akan mendapatkannya kecuali Anda menginterogasi Pengecualian yang dilemparkan. Alasannya adalah bahwa ConvertFromString melempar OverflowException, dan kemudian para pemain ke T melempar System.Exception.
Trevor

78

Saya juga membutuhkan TryParse generik baru-baru ini. Inilah yang saya pikirkan;

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

Maka itu hanya masalah memanggil demikian:

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

3
Baru saja menemukan posting ini lagi beberapa bulan kemudian dan memperhatikan saat menggunakannya lagi bahwa metode ini tidak dapat disimpulkan Tdari handler, dan kita harus secara eksplisit menentukan Tkapan kita menyebutnya. Saya ingin tahu, mengapa itu tidak bisa disimpulkan T?
Nick Strupat

25
Mengapa Anda ingin menggunakan fungsi ini? Jika Anda tahu fungsi mana yang dipanggil untuk mem-parsing nilai, mengapa tidak langsung memanggilnya? Itu sudah tahu tipe input yang tepat dan tidak perlu untuk obat generik. Solusi ini tidak akan bekerja untuk jenis tanpa TryParseHandler.
xxbbcc

2
@xxbbcc: Saya ingin menggunakan fungsi ini karena TryParse mengembalikan boolean yang menunjukkan jika parse berhasil. Ini mengembalikan nilai parsing Anda melalui parameter output. Terkadang saya hanya ingin melakukan sesuatu seperti ini SomeMethod(TryParse<int>(DollarTextbox.Text, int.TryParse))tanpa membuat variabel output untuk menangkap hasilnya int.TryParse. Namun, saya setuju dengan sentimen Nick tentang memiliki fungsi menyimpulkan tipe.
Walter Stabosz

1
Metode yang sangat efisien. Sangat dianjurkan.
Vladimir Kocjancic

3
Saya akan merekomendasikan nilai default sebagai param ketiga. Itu memperbaiki masalah di mana T tidak dapat disimpulkan. Juga, ini memungkinkan seseorang untuk memutuskan nilai apa yang mereka inginkan jika nilai string tidak valid. Misalnya, -1 mungkin berarti tidak valid. public static T TryParse <T> (nilai string, TryParseHandler <T> handler, T defaultValue)
Rhyous

33

Menggunakan coba / tangkap untuk kontrol aliran adalah kebijakan yang mengerikan. Melempar pengecualian menyebabkan kelambatan kinerja sementara runtime bekerja di sekitar pengecualian. Alih-alih memvalidasi data sebelum mengonversi.

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
Saya mendapatkan pemberitahuan Resharper yang converter != nullselalu benar, sehingga dapat dihapus dari kode.
ErikE

5
@ErikE Saya tidak selalu mempercayai peringatan ReSharper itu. Seringkali mereka tidak dapat melihat apa yang terjadi pada saat runtime.
ProfK

1
@ProfK MSDN tidak mengatakan bahwa ia dapat mengembalikan nol msdn.microsoft.com/en-us/library/ewtxwhzx.aspx
danio

@danio Saya baru saja membagikan pengalaman saya dengan peringatan R # seperti itu secara umum. Saya tentu tidak menyiratkan bahwa itu salah dalam kasus ini.
ProfK

14

Jika Anda menggunakan TryParse, Anda dapat menggunakan refleksi dan melakukannya seperti ini:

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

Ini sangat keren, dan itu menghilangkan pengecualian yang saya tidak suka. Masih agak berbelit-belit.
Piers Myers

6
Solusi yang bagus, tetapi jawaban apa pun yang melibatkan refleksi (terutama dalam metode utilitas yang dapat dengan mudah dipanggil dari loop internal) memerlukan penafian tentang kinerja. Lihat: stackoverflow.com/questions/25458/how-costly-is-net-reflection
Patrick M

Mendesah. Jadi pilihannya adalah (1) menggunakan pengecualian untuk kontrol aliran kode, (2) menggunakan refleksi dengan biaya kecepatannya. Saya setuju dengan @PiersMyers - tidak ada pilihan yang ideal. Untung mereka berdua bekerja. :)
Jesse Chisholm

Saya pikir Anda dapat mengganti Type.GetType(string.Format(...))dengan type.MakeByRefType().
Drew Noakes

3
metode hanya perlu direfleksikan sekali per jenis, bukan sekali per panggilan. jika Anda menjadikan ini kelas generik dengan variabel anggota statis, maka Anda bisa menggunakan kembali hasil refleksi pertama.
Andrew Hill

7

Ini menggunakan konstruktor statis untuk setiap jenis generik, sehingga hanya perlu melakukan pekerjaan mahal saat pertama kali Anda menyebutnya pada jenis tertentu. Ini menangani semua jenis dalam namespace sistem yang memiliki metode TryParse. Ini juga bekerja dengan versi nullable dari masing-masing (yang merupakan struct) kecuali untuk enumerasi.

    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

Bagaimana dengan sesuatu yang seperti ini?

http://madskristensen.net/post/Universal-data-type-checker.aspx ( Arsip )

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

Ini dapat dikonversi menjadi metode generik dengan mudah.

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

Apakah penting apakah Anda mengembalikan true dari blok try atau return false dari blok catch? Saya kira tidak, tapi saya masih berpikir menggunakan pengecualian dengan cara ini terasa salah bagi saya ...
Piers Myers

3
Tidak masalah apakah Anda kembali dari blok penangkap, ini sama. btw. Biasanya itu buruk untuk memiliki klausa catch generik: catch { }. Namun, dalam hal ini tidak ada alternatif, karena .NET BaseNumberConvertermelempar Exceptionkelas dasar jika terjadi kesalahan konversi. Ini sangat disayangkan. Bahkan masih ada beberapa tempat dimana jenis pangkalan ini dilempar. Semoga Microsoft akan memperbaikinya dalam versi kerangka kerja yang akan datang.
Steven

Terima kasih Steven, Tidak mungkin mengatakannya lebih baik.
Bob

Tidak ada hasil konversi yang digunakan: kode berlebihan.
BillW

4

Anda tidak dapat melakukannya pada tipe umum.

Apa yang bisa Anda lakukan adalah membuat antarmuka ITryParsable dan menggunakannya untuk tipe kustom yang mengimplementasikan antarmuka ini.

Saya kira Anda berniat untuk menggunakan ini dengan tipe dasar seperti intdan DateTime. Anda tidak dapat mengubah jenis ini untuk mengimplementasikan antarmuka baru.


1
Saya ingin tahu apakah itu akan berhasil dengan menggunakan kata kunci dinamis di .net 4?
Pierre-Alain Vigeant

@Pierre: Ini tidak akan berfungsi secara default di C # dengan dynamickata kunci, karena itu tidak akan berfungsi pada pengetikan statis. Anda dapat membuat objek dinamis Anda sendiri yang bisa menangani ini, tetapi itu bukan default.
Steven

4

Terinspirasi oleh solusi yang diposting di sini oleh Charlie Brown, saya membuat TryParse generik menggunakan refleksi yang secara opsional menampilkan nilai yang diuraikan:

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

Ini bisa disebut sebagai:

string input = "123";
decimal myDecimal;

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

Pembaruan:
Juga berkat solusi YotaXP yang sangat saya sukai, saya membuat versi yang tidak menggunakan metode ekstensi tetapi masih memiliki singleton, meminimalkan kebutuhan untuk melakukan refleksi:

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

Sebut saja seperti ini:

string input = "987";
decimal myDecimal;

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

3

Agak terlambat ke pesta, tapi inilah yang saya pikirkan. Tanpa pengecualian, pantulan satu kali (per jenis).

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

Kelas tambahan diperlukan karena metode ekstensi tidak diizinkan di dalam kelas generik. Ini memungkinkan penggunaan sederhana, seperti yang ditunjukkan di bawah ini, dan hanya hits refleksi saat pertama kali suatu jenis digunakan.

"5643".ParseAs<int>()

3

Ini pilihan lain.

Saya menulis sebuah kelas yang memudahkan untuk mendaftarkan sejumlah TryParsepenangan. Ini memungkinkan saya melakukan ini:

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

Saya 42dicetak ke konsol.

Kelasnya adalah:

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

Saya suka ini, tetapi bagaimana Anda melakukannya tanpa obat generik. Gunakan case sebagai refleksi, tentu saja.
Sinaesthetic

Saya menambahkan metode kelebihan beban yang melakukan beberapa hackery refleksi. Jika ada cara yang lebih elegan untuk mengatasinya, saya semua mata lol gist.github.com/dasjestyr/90d8ef4dea179a6e08ddd85e0dacbc94
Sinaesthetic

2

Ketika saya ingin melakukan hampir hal yang tepat ini, saya harus menerapkannya dengan cara yang keras, mengingat refleksi. Diberikan T, merenungkan typeof(T)dan mencari suatu TryParseatau Parsemetode, memohonnya jika Anda telah menemukannya.


Inilah yang akan saya sarankan.
Steven Evers

2

Ini usaha saya. Saya melakukannya sebagai "latihan". Saya mencoba membuatnya mirip dengan yang digunakan sebagai " Convert.ToX () " yang sudah ada - dll. Tapi ini adalah metode ekstensi:

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

Kerugian utama dibandingkan dengan TypeConverter.ConvertFrom()ini adalah bahwa kelas sumber harus menyediakan konversi tipe, yang umumnya berarti Anda tidak dapat mendukung konversi ke tipe kustom.
Ian Goldby

1

Seperti yang Anda katakan, TryParsebukan bagian dari antarmuka. Ini juga bukan anggota dari kelas dasar yang diberikan karena sebenarnya staticdan staticfungsi tidak bisa virtual. Jadi, kompiler tidak memiliki cara untuk memastikan bahwa Tsebenarnya memiliki anggota yang dipanggilTryParse , jadi ini tidak berfungsi.

Seperti yang dikatakan @Mark, Anda dapat membuat antarmuka sendiri dan menggunakan jenis khusus, tetapi Anda kurang beruntung untuk jenis bawaan.


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

Ini adalah pertanyaan tentang 'kendala umum'. Karena Anda tidak memiliki antarmuka spesifik maka Anda macet kecuali Anda mengikuti saran dari jawaban sebelumnya.

Untuk dokumentasi tentang ini, periksa tautan berikut:

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

Ini menunjukkan kepada Anda bagaimana menggunakan batasan-batasan ini dan harus memberi Anda lebih banyak petunjuk.


0

Dipinjam dari http://blogs.msdn.com/b/davidebb/archive/2009/10/23/using-c-dynamic-to-call-static-members.aspx

ketika mengikuti referensi ini: Bagaimana memanggil metode statis di C # 4.0 dengan tipe dinamis?

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

Dan gunakan sebagai berikut:

  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

Saya berhasil mendapatkan sesuatu yang berfungsi seperti ini

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

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

Ini kode saya

 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 diadaptasi dari artikel David Ebbo (itu melontarkan AmbiguousMatchException)


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

0

Dengan TypeDescriptorpenggunaan kelas TryParseterkait:

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

Sementara kode ini dapat menyelesaikan pertanyaan, termasuk penjelasan tentang bagaimana dan mengapa ini menyelesaikan masalah akan sangat membantu untuk meningkatkan kualitas posting Anda, dan mungkin menghasilkan lebih banyak suara. Ingatlah bahwa Anda menjawab pertanyaan untuk pembaca di masa depan, bukan hanya orang yang bertanya sekarang. Harap edit jawaban Anda untuk menambahkan penjelasan dan berikan indikasi tentang batasan dan asumsi apa yang berlaku.
bunyi bip ganda

0

Menggunakan informasi di atas, inilah yang saya kembangkan. Itu akan mengkonversi objek secara langsung adalah mungkin, jika tidak akan mengkonversi objek ke string dan memanggil metode TryParse untuk jenis objek yang diinginkan.

Saya cache metode dalam kamus karena masing-masing ditemui untuk mengurangi metode pengambilan beban.

Mungkin untuk menguji apakah objek dapat langsung dikonversi ke jenis target, yang selanjutnya akan mengurangi bagian konversi string. Tapi saya akan meninggalkan itu untuk saat ini.

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

Saya harus menambahkan fungsi lain untuk mendukung enumerasi. Sepertinya parsing Enum memerlukan atribut "where T: struct", dan saya ingin ini berfungsi pada apa pun yang dapat dikonversi. (mungkin harus menambahkan atribut convertible ke tipe). Namun, beberapa saran berikut terlihat lebih sederhana (jadi lebih baik).
B Duffy

0

Saya mengumpulkan banyak ide di sini bersama dan berakhir dengan solusi yang sangat singkat.

Ini adalah metode ekstensi pada string

enter code here

Saya membuatnya dengan cetakan kaki yang sama dengan metode TryParse pada tipe numerik

    /// <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);

    }

''


float testValue = 0; if ("1234" .TryParse <float> (out testValue)) {doSomethingGood (); } else {handleTheBadness (); }
JD Hicks

0

T.TryParse ... mengapa?

Saya melihat tidak ada manfaatnya memiliki TryParsefungsi generik seperti itu . Ada terlalu banyak strategi berbeda untuk mem-parsing dan mengonversi data di antara berbagai tipe, dengan kemungkinan perilaku yang saling bertentangan. Bagaimana fungsi ini bisa tahu strategi mana yang harus dipilih dalam mode bebas konteks?

  • kelas dengan fungsi TryParse khusus dapat dipanggil
  • kelas dengan fungsi Parse khusus dapat dibungkus dengan hasil try-catch dan bool
  • kelas dengan kelebihan operator, bagaimana Anda membiarkan mereka menangani parsing?
  • deskriptor tipe sudah terpasang Convert.ChangeType . API ini dapat dikustomisasi saat runtime. Apakah fungsi Anda memerlukan perilaku default atau memungkinkan penyesuaian?
  • haruskah Anda mengizinkan kerangka pemetaan untuk mencoba menguraikan untuk Anda?
  • bagaimana Anda menangani konflik di atas?

-2

Versi untuk mendapatkan keturunan dari XDocument.

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