Cara mengurai string ke int nullable

300

Saya ingin mengurai string menjadi int nullable di C #. yaitu. Saya ingin mendapatkan kembali nilai int string atau null jika tidak dapat diuraikan.

Saya agak berharap ini akan berhasil

int? val = stringVal as int?;

Tetapi itu tidak akan berhasil, jadi cara saya melakukannya sekarang adalah saya telah menulis metode ekstensi ini

public static int? ParseNullableInt(this string value)
{
    if (value == null || value.Trim() == string.Empty)
    {
        return null;
    }
    else
    {
        try
        {
            return int.Parse(value);
        }
        catch
        {
            return null;
        }
    }
}   

Apakah ada cara yang lebih baik untuk melakukan ini?

EDIT: Terima kasih atas saran TryParse, saya memang tahu tentang itu, tetapi ternyata sama. Saya lebih tertarik mengetahui apakah ada metode kerangka kerja bawaan yang akan menguraikan secara langsung ke int nullable?

Glenn Slaven
sumber
1
Anda dapat menggunakan string.IsNullOrEmpty (nilai) untuk mendapatkan garis if lebih jelas.
Özgür Kaplan
Pertimbangkan untuk menggunakan konversi generik stackoverflow.com/questions/773078/…
Michael Freidgeim

Jawaban:

352

int.TryParse mungkin sedikit lebih mudah:

public static int? ToNullableInt(this string s)
{
    int i;
    if (int.TryParse(s, out i)) return i;
    return null;
}

Edit @Glenn int.TryParseadalah "dibangun ke dalam kerangka kerja". Dan int.Parseyang satu cara untuk mengurai string ke int.

Matt Hamilton
sumber
82
satu baris kurang: kembali Int32.TryParse (s, out i)? i: null;
Chris Shouts
2
"a" akan mengembalikan nol, tetapi tidak int dan harus membuang pengecualian
Arsen Mkrtchyan
54
@ Chris, kompiler tidak suka inline Anda jika pernyataan (Tipe-tipe ini tidak kompatibel: 'int': 'null'). Saya harus mengubahnya menjadi: mengembalikan Int32.TryParse (s, out i)? (int?) i: null;
death_au
8
Int32 hanyalah alias untuk int. Saya akan menggunakan int.TryParse untuk menjaga tipe yang digunakan sejalan. Jika / ketika int digunakan untuk mewakili integer panjang bit yang berbeda (yang telah terjadi), Int32 tidak akan sejajar dengan int.
Richard Collette
4
mengembalikan int.TryParse (s, out i)? (int?) i: null;
Nick Spreitzer
178

Anda dapat melakukan ini dalam satu baris, menggunakan operator kondisional dan fakta bahwa Anda dapat menggunakan nulltipe nullable (dua baris, jika Anda tidak memiliki int yang sudah ada, Anda dapat menggunakan kembali untuk output dari TryParse):

Pra C # 7:

int tempVal;
int? val = Int32.TryParse(stringVal, out tempVal) ? Int32.Parse(stringVal) : (int?)null;

Dengan sintaks diperbarui C # 7 yang memungkinkan Anda untuk mendeklarasikan variabel output dalam pemanggilan metode, ini menjadi lebih sederhana.

int? val = Int32.TryParse(stringVal, out var tempVal) ? tempVal : (int?)null;
McKenzieG1
sumber
4
Itu tergantung pada pandangan Anda tentang operator bersyarat, saya pikir. Model mental saya adalah bahwa itu cukup banyak gula sintaksis untuk padanan if-else, dalam hal ini versi saya dan Matt hampir identik, dengan yang lebih eksplisit, milikku lebih cmopact.
McKenzieG1
11
Tidak ada efek samping urutan evaluasi di sini. Semua langkah dipesan secara eksplisit dan benar.
Jon Hanna
22
kembaliint.TryParse(val, out i) ? i : default(int?);
Bart Calixto
7
"Jawaban" Bart adalah yang terbaik di sini!
Andre Figueiredo
4
Dan sekarang di C # 6, itu bisa menjadi satu baris! Int32.TryParse (stringVal, out var tempVal)? tempVal: (int?) null;
MerickOWA
34

[ Diperbarui untuk menggunakan C # modern sesuai saran @ sblom]

Saya punya masalah ini dan saya berakhir dengan ini (setelah semua, an ifdan 2 returns sangat bertele-tele!):

int? ToNullableInt (string val)
    => int.TryParse (val, out var i) ? (int?) i : null;

Pada catatan yang lebih serius, cobalah untuk tidak mencampur int, yang merupakan kata kunci C #, dengan Int32, yang merupakan tipe .NET Framework BCL - meskipun berfungsi, itu hanya membuat kode terlihat berantakan.

Duckboy
sumber
3
Tidak yakin ini akan benar-benar diterjemahkan ke dalam apa pun yang berkinerja lebih baik setelah dikompilasi
BuZz
1
Yang lebih ringkas di C # 7: hapus int i;baris dan langsung sajareturn int.TryParse (val, out var i) ? (int?) i : null;
sblom
2
Jadi untuk kelengkapan ;-)int? ParseNInt (string val) => int.TryParse (val, out var i) ? (int?) i : null;
Duckboy
Dengan C # 6 ini dapat dikurangi menjadi 1 baris: return int.TryParse (nilai, hasil out var)? hasil: (int?) null;
MeanGreen
16

Glenn Slaven : Saya lebih tertarik mengetahui apakah ada metode kerangka kerja bawaan yang akan mem-parsing langsung ke int nullable?

Ada pendekatan ini yang akan menguraikan secara langsung ke int nullable (dan bukan hanya int) jika nilainya valid seperti null atau string kosong, tetapi tidak membuang pengecualian untuk nilai yang tidak valid sehingga Anda harus menangkap pengecualian dan mengembalikan nilai default untuk situasi tersebut:

public static T Parse<T>(object value)
{
    try { return (T)System.ComponentModel.TypeDescriptor.GetConverter(typeof(T)).ConvertFrom(value.ToString()); }
    catch { return default(T); }
}

Pendekatan ini masih dapat digunakan untuk parse yang tidak dapat dibatalkan maupun nullable:

enum Fruit { Orange, Apple }
var res1 = Parse<Fruit>("Apple");
var res2 = Parse<Fruit?>("Banana");
var res3 = Parse<int?>("100") ?? 5; //use this for non-zero default
var res4 = Parse<Unit>("45%");

NB: Ada metode IsValid pada konverter yang dapat Anda gunakan alih-alih menangkap pengecualian (pengecualian yang dilemparkan tidak menghasilkan overhead yang tidak perlu jika diharapkan). Sayangnya itu hanya bekerja sejak .NET 4 tetapi masih ada masalah di mana ia tidak memeriksa lokal Anda ketika memvalidasi format DateTime yang benar, lihat bug 93559 .

Michael
sumber
Saya menguji ini untuk bilangan bulat dan itu jauh lebih lambat daripada nilai int.TryParse ((string), keluar hasil var)? hasil: default (int?);
Wouter
12
var result = int.TryParse(foo, out var f) ? f : default(int?);

Sumber:

Jaa H
sumber
bagaimana ini bisa berhasil? Tryparse tidak akan berfungsi atau variabel nullable dan f dalam contoh Anda harus nullable.
John Lord
Tolong bisakah Anda mengklarifikasi maksud Anda @JohnLord
Jaa H
tryparse mengharapkan untuk dimasukkan ke dalam variabel yang tidak dapat dibatalkan, jadi bukankah default (int?) Anda memaksa var menjadi nullable?
John Lord
@JohnLord mungkin ini akan membantu Anda memahami apa yang terjadi di stackoverflow.com/questions/3632918/…
Jaa H
9

Topik lama, tapi bagaimana dengan:

public static int? ParseToNullableInt(this string value)
{
     return String.IsNullOrEmpty(value) ? null : (int.Parse(value) as int?);
}

Saya suka ini lebih baik sebagai persyaratan tempat parse null, versi TryParse tidak akan melempar kesalahan pada misalnya ToNullableInt32 (XXX). Itu dapat menyebabkan kesalahan diam yang tidak diinginkan.

mortb
sumber
1
Itulah intinya - jika string tidak dapat diuraikan int, itu harus kembali null, bukan melemparkan pengecualian.
svick
1
jika nilainya non-numerik, int.Parse melempar pengecualian, yang tidak sama dengan mengembalikan nol.
sebuah phu
8

Coba ini:

public static int? ParseNullableInt(this string value)
{
    int intValue;
    if (int.TryParse(value, out intValue))
        return intValue;
    return null;
}
Joseph Daigle
sumber
5

Saya merasa solusi saya adalah solusi yang sangat bersih dan menyenangkan:

public static T? NullableParse<T>(string s) where T : struct
{
    try
    {
        return (T)typeof(T).GetMethod("Parse", new[] {typeof(string)}).Invoke(null, new[] { s });
    }
    catch (Exception)
    {
        return null;
    }
}

Ini tentu saja merupakan solusi generik yang hanya mengharuskan argumen generik memiliki metode statis "Parse (string)". Ini berfungsi untuk angka, boolean, DateTime, dll.

Lyskespark
sumber
5

Anda dapat melupakan semua jawaban lain - ada solusi umum yang hebat: http://cleansharp.de/wordpress/2011/05/generischer-typeconverter/

Ini memungkinkan Anda untuk menulis kode yang sangat bersih seperti ini:

string value = null;
int? x = value.ConvertOrDefault();

dan juga:

object obj = 1;  

string value = null;
int x = 5;
if (value.TryConvert(out x))
    Console.WriteLine("TryConvert example: " + x); 

bool boolean = "false".ConvertOrDefault();
bool? nullableBoolean = "".ConvertOrDefault();
int integer = obj.ConvertOrDefault();
int negativeInteger = "-12123".ConvertOrDefault();
int? nullableInteger = value.ConvertOrDefault();
MyEnum enumValue = "SecondValue".ConvertOrDefault();

MyObjectBase myObject = new MyObjectClassA();
MyObjectClassA myObjectClassA = myObject.ConvertOrDefault();
Pavel Hodek
sumber
1
Ini memang sangat bermanfaat. Menurut pendapat saya ini harus di perpustakaan c # standar karena konversi sangat umum di setiap program;)
BigChief
Ini sangat bagus dan bermanfaat, TETAPI saya dapat menambahkan bahwa itu sangat lambat ketika perlu membuat konversi untuk setiap elemen dalam koleksi item yang besar. Saya telah menguji dengan 20.000 item: dengan menggunakan pendekatan ini, mengonversi 8 properti dari setiap item membutuhkan waktu hingga 1 jam untuk menyelesaikan seluruh koleksi. Dengan data sampel yang sama tetapi menggunakan pendekatan Matt Hamilton, hanya perlu beberapa detik untuk menyelesaikannya.
zed
3

Yang berikut ini harus bekerja untuk semua tipe struct. Ini didasarkan dari kode oleh Matt Manela dari forum MSDN . Seperti Murph tunjukkan penanganan pengecualian bisa mahal dibandingkan dengan menggunakan metode TryParse Jenis khusus.

        public static bool TryParseStruct<T>(this string value, out Nullable<T> result)
            where T: struct 
        {
            if (string.IsNullOrEmpty(value))
            {
                result = new Nullable<T>();

                return true;
            }

            result = default(T);
            try
            {
                IConvertible convertibleString = (IConvertible)value;
                result = new Nullable<T>((T)convertibleString.ToType(typeof(T), System.Globalization.CultureInfo.CurrentCulture));
            }
            catch(InvalidCastException)
            {
                return false;
            }
            catch (FormatException)
            {
                return false;
            }

           return true;
        }

Ini adalah kasus uji dasar yang saya gunakan.

        string parseOne = "1";
        int? resultOne;
        bool successOne = parseOne.TryParseStruct<int>(out resultOne);
        Assert.IsTrue(successOne);
        Assert.AreEqual(1, resultOne);

        string parseEmpty = string.Empty;
        int? resultEmpty;
        bool successEmpty = parseEmpty.TryParseStruct<int>(out resultEmpty);
        Assert.IsTrue(successEmpty);
        Assert.IsFalse(resultEmpty.HasValue);

        string parseNull = null;
        int? resultNull;
        bool successNull = parseNull.TryParseStruct<int>(out resultNull);
        Assert.IsTrue(successNull);
        Assert.IsFalse(resultNull.HasValue);

        string parseInvalid = "FooBar";
        int? resultInvalid;
        bool successInvalid = parseInvalid.TryParseStruct<int>(out resultInvalid);
        Assert.IsFalse(successInvalid);
Daniel Ballinger
sumber
3

Saya akan menyarankan metode ekstensi berikut untuk string parsing ke nilai int dengan kemampuan untuk menentukan nilai default jika parsing tidak mungkin:

public static int ParseInt(this string value, int defaultIntValue = 0)
        {
            return int.TryParse(value, out var parsedInt) ? parsedInt : defaultIntValue;
        }

public static int? ParseNullableInt(this string value)
        {
            if (string.IsNullOrEmpty(value))
                return null;

            return value.ParseInt();
        }
Aleksandr Neizvestnyi
sumber
Sudah ada begitu banyak dan bahkan jawaban yang dijunjung tinggi. Apakah Anda benar-benar berpikir jawaban Anda diperlukan dan menambah kualitas baru pada posting ini?
L. Guthardt
1
@ L.Guthardt Ya, saya kira begitu. Karena saya pikir jawaban saya membawa cara yang lebih universal untuk menyelesaikan masalah yang dijelaskan dalam pertanyaan. Terima kasih.
Aleksandr Neizvestnyi
2

Solusi ini generik tanpa overhead refleksi.

public static Nullable<T> ParseNullable<T>(string s, Func<string, T> parser) where T : struct
{
    if (string.IsNullOrEmpty(s) || string.IsNullOrEmpty(s.Trim())) return null;
    else return parser(s);
}

static void Main(string[] args)
{
    Nullable<int> i = ParseNullable("-1", int.Parse);
    Nullable<float> dt = ParseNullable("3.14", float.Parse);
}
Qi Luo
sumber
Saya pikir Anda dapat mengganti IsNullOrEmptydenganIsNullOrWhitespace
NibblyPig
1

Saya lebih tertarik mengetahui apakah ada metode kerangka kerja bawaan yang akan menguraikan secara langsung ke int nullable?

Tidak ada.

Orion Edwards
sumber
1
Apakah Anda menganggap ini pendekatan langsung? stackoverflow.com/a/6474962/222748
Michael
1

Saya merasa saya harus membagikan milik saya yang sedikit lebih umum.

Pemakaian:

var result = "123".ParseBy(int.Parse);

var result2 = "123".ParseBy<int>(int.TryParse);

Larutan:

public static class NullableParse
{
    public static Nullable<T> ParseBy<T>(this string input, Func<string, T> parser)
        where T : struct
    {
        try
        {
            return parser(input);
        }
        catch (Exception exc)
        {
            return null;
        }
    }

    public delegate bool TryParseDelegate<T>(string input, out T result);

    public static Nullable<T> ParseBy<T>(this string input, TryParseDelegate<T> parser)
        where T : struct
    {
        T t;
        if (parser(input, out t)) return t;
        return null;
    }
}

Versi pertama lebih lambat karena membutuhkan coba-coba tetapi terlihat lebih bersih. Jika tidak akan dipanggil berkali-kali dengan string yang tidak valid, itu tidak penting. Jika kinerja merupakan masalah, harap perhatikan bahwa saat menggunakan metode TryParse, Anda perlu menentukan parameter tipe ParseBy karena tidak dapat disimpulkan oleh kompiler. Saya juga harus mendefinisikan delegasi sebagai kata kunci keluar tidak dapat digunakan dalam Fungsi <>, tetapi setidaknya kali ini kompiler tidak memerlukan contoh eksplisit.

Akhirnya, Anda dapat menggunakannya dengan struct lain juga, yaitu desimal, DateTime, Guid, dll.

orcun
sumber
1

Saya menemukan dan mengadaptasi beberapa kode untuk kelas Generic NullableParser. Kode lengkapnya ada di blog saya Nullable TryParse

using System;
using System.Collections.Generic;
using System.Linq;
using System.Text;
using System.Globalization;
namespace SomeNamespace
{
    /// <summary>
    /// A parser for nullable types. Will return null when parsing fails.
    /// </summary>
    /// <typeparam name="T"></typeparam>
    ///
    public static class NullableParser<T> where T : struct
    {
        public delegate bool TryParseDelegate(string s, out T result);
        /// <summary>
        /// A generic Nullable Parser. Supports parsing of all types that implements the tryParse method;
        /// </summary>
        /// <param name="text">Text to be parsed</param>
        /// <param name="result">Value is true for parse succeeded</param>
        /// <returns>bool</returns>
        public static bool TryParse(string s, out Nullable<T> result)
        {
            bool success = false;
            try
            {
                if (string.IsNullOrEmpty(s))
                {
                    result = null;
                    success = true;
                }
                else
                {
                    IConvertible convertableString = s as IConvertible;
                    if (convertableString != null)
                    {
                        result = new Nullable<T>((T)convertableString.ToType(typeof(T),
                            CultureInfo.CurrentCulture));
                        success = true;
                    }
                    else
                    {
                        success = false;
                        result = null;
                    }
                }
            }
            catch
            {
                success = false;
                result = null;
            }
            return success;
        }
    }
}
John Dauphine
sumber
1
404 tidak ditemukan. itu bukan praktik yang baik hanya dengan memberikan tautan
Dirty-flow
maaf tentang itu @ Pembaruan aliran kotor dengan kode lengkap. Lebih baik terlambat daripada tidak pernah :)
John Dauphine
1
    public static void Main(string[] args)
    {

        var myString = "abc";

        int? myInt = ParseOnlyInt(myString);
        // null

        myString = "1234";

        myInt = ParseOnlyInt(myString);
        // 1234
    }
    private static int? ParseOnlyInt(string s)
    {
        return int.TryParse(s, out var i) ? i : (int?)null;
    }
Crivelli
sumber
1
jika myString non-numerik, int.Parse melempar pengecualian, yang tidak sama dengan mengembalikan nol.
sebuah phu
0

Anda seharusnya tidak pernah menggunakan pengecualian jika Anda tidak perlu - overhead itu mengerikan.

Variasi pada TryParse menyelesaikan masalah - jika Anda ingin menjadi kreatif (untuk membuat kode Anda terlihat lebih elegan) Anda mungkin bisa melakukan sesuatu dengan metode ekstensi dalam 3,5 tetapi kode akan kurang lebih sama.

Murph
sumber
0

Menggunakan delegasi, kode berikut ini dapat memberikan reusability jika Anda membutuhkan parsing nullable untuk lebih dari satu tipe struktur. Saya telah menunjukkan versi .Parse () dan .TryParse () di sini.

Ini adalah contoh penggunaan:

NullableParser.TryParseInt(ViewState["Id"] as string);

Dan di sini adalah kode yang membuat Anda di sana ...

public class NullableParser
  {
    public delegate T ParseDelegate<T>(string input) where T : struct;
    public delegate bool TryParseDelegate<T>(string input, out T outtie) where T : struct;
    private static T? Parse<T>(string input, ParseDelegate<T> DelegateTheParse) where T : struct
    {
      if (string.IsNullOrEmpty(input)) return null;
      return DelegateTheParse(input);
    }
    private static T? TryParse<T>(string input, TryParseDelegate<T> DelegateTheTryParse) where T : struct
    {
      T x;
      if (DelegateTheTryParse(input, out x)) return x;
      return null;
    }
    public static int? ParseInt(string input)
    {
      return Parse<int>(input, new ParseDelegate<int>(int.Parse));
    }
    public static int? TryParseInt(string input)
    {
      return TryParse<int>(input, new TryParseDelegate<int>(int.TryParse));
    }
    public static bool? TryParseBool(string input)
    {
      return TryParse<bool>(input, new TryParseDelegate<bool>(bool.TryParse));
    }
    public static DateTime? TryParseDateTime(string input)
    {
      return TryParse<DateTime>(input, new TryParseDelegate<DateTime>(DateTime.TryParse));
    }
  }
umbyersw
sumber
0

Saya menyadari ini adalah topik lama, tetapi tidak bisakah Anda hanya:

(Nullable<int>)int.Parse(stringVal);

?

Leigh Bowers
sumber
Anda bisa, tetapi kemudian Anda akan mendapatkan pengecualian jika stringVal dalam format yang salah. Lihat dokumentasi int.Parse: msdn.microsoft.com/en-us/library/b3h1hf19.aspx
Alex
0

Saya telah datang dengan yang ini, yang telah memenuhi persyaratan saya (saya ingin metode ekstensi saya untuk meniru sedekat mungkin kembalinya TryParse kerangka kerja, tetapi tanpa blok mencoba {} catch {} dan tanpa kompiler mengeluh tentang menyimpulkan sebuah jenis nullable dalam metode kerangka kerja)

private static bool TryParseNullableInt(this string s, out int? result)
{
    int i;
    result = int.TryParse(s, out i) ? (int?)i : null;
    return result != null;
}
wmoecke
sumber
0

Saya sarankan kode di bawah ini. Anda dapat bekerja dengan pengecualian, saat kesalahan konversi terjadi.

public static class Utils {      
public static bool TryParse<Tin, Tout>(this Tin obj, Func<Tin, Tout> onConvert, Action<Tout> onFill, Action<Exception> onError) {
  Tout value = default(Tout);
  bool ret = true;
  try {
    value = onConvert(obj);
  }
  catch (Exception exc) {
    onError(exc);
    ret = false;
  }
  if (ret)
    onFill(value);
  return ret;
}

public static bool TryParse(this string str, Action<int?> onFill, Action<Exception> onError) {
  return Utils.TryParse(str
    , s => string.IsNullOrEmpty(s) ? null : (int?)int.Parse(s)
    , onFill
    , onError);
}
public static bool TryParse(this string str, Action<int> onFill, Action<Exception> onError) {
  return Utils.TryParse(str
    , s => int.Parse(s)
    , onFill
    , onError);
}
}

Gunakan metode ekstensi ini dalam kode (isi properti? Umur dari kelas orang):

string ageStr = AgeTextBox.Text;
Utils.TryParse(ageStr, i => person.Age = i, exc => { MessageBox.Show(exc.Message); });

ATAU

AgeTextBox.Text.TryParse(i => person.Age = i, exc => { MessageBox.Show(exc.Message); });
lison
sumber