Làm thế nào để phân tích một chuỗi thành một int nullable


300

Tôi muốn phân tích một chuỗi thành một int nullable trong C #. I E. Tôi muốn lấy lại giá trị int của chuỗi hoặc null nếu không thể phân tích cú pháp.

Tôi đã hy vọng rằng điều này sẽ làm việc

int? val = stringVal as int?;

Nhưng điều đó sẽ không hiệu quả, vì vậy cách tôi đang làm bây giờ là tôi đã viết phương pháp mở rộng này

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

Có cách nào tốt hơn để làm điều này?

EDIT: Cảm ơn các đề xuất của TryPude, tôi đã biết về điều đó, nhưng nó đã giải quyết được vấn đề tương tự. Tôi quan tâm hơn đến việc biết liệu có một phương thức khung dựng sẵn sẽ phân tích trực tiếp thành một int nullable không?


1
Bạn có thể sử dụng chuỗi.IsNullOrEmpty (value) để có được dòng if rõ ràng hơn.
Özgür Kaplan

Câu trả lời:


352

int.TryParse có lẽ là một chút dễ dàng hơn:

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

Chỉnh sửa @Glenn int.TryParselà "được xây dựng trong khung". Nó và int.Parsenhững cách để phân tích chuỗi để ints.


82
một dòng ít hơn: return Int32.TryPude (s, out i)? i: không;
Chris hét lên

2
"a" sẽ trả về null, nhưng nó không phải là int và nên ném ngoại lệ
Arsen Mkrtchyan

54
@Chris, trình biên dịch không thích câu lệnh if nội tuyến của bạn (Các loại này không tương thích: 'int': 'null'). Tôi đã phải sửa đổi nó thành: return Int32.TryPude (s, out i)? (int?) i: null;
death_au

8
Int32 chỉ là một bí danh cho int. Tôi sẽ sử dụng int.TryPude để giữ các loại được sử dụng trong căn chỉnh. Nếu / khi int được sử dụng để biểu diễn một số nguyên có độ dài bit khác nhau (đã xảy ra), Int32 sẽ không xếp hàng với int.
Richard Collette

4
return int.TryPude (s, out i)? (int?) i: null;
Nick Spreitzer

178

Bạn có thể thực hiện việc này trong một dòng, sử dụng toán tử có điều kiện và thực tế là bạn có thể nullchuyển sang loại không thể (hai dòng, nếu bạn không có int int tồn tại trước, bạn có thể sử dụng lại cho đầu ra của TryParse):

Trước C # 7:

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

Với cú pháp được cập nhật của C # 7 cho phép bạn khai báo một biến đầu ra trong lệnh gọi phương thức, điều này thậm chí còn đơn giản hơn.

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

4
Điều đó phụ thuộc vào quan điểm của bạn về toán tử có điều kiện, tôi nghĩ vậy. Mô hình tinh thần của tôi là nó có khá nhiều đường cú pháp cho tương đương if-other, trong trường hợp đó phiên bản của tôi và Matt gần giống nhau, với sự rõ ràng hơn của anh ấy, tôi có nhiều cmopact hơn.
McKenzieG1

11
Không có tác dụng phụ đánh giá thứ tự ở đây. Tất cả các bước được sắp xếp rõ ràng và chính xác.
Jon Hanna

22
trở lạiint.TryParse(val, out i) ? i : default(int?);
Bart Calixto

7
"Câu trả lời" của Bart là tốt nhất ở đây!
Andre Figueiredo

4
Và bây giờ trong C # 6, nó có thể là một dòng! Int32.TryPude (stringVal, out var tempVal)? tempVal: (int?) null;
MerickOWA

34

[ Đã cập nhật để sử dụng C # hiện đại theo đề xuất của @ sblom]

Tôi đã có vấn đề này và tôi đã kết thúc với vấn đề này (sau tất cả, một ifvà 2 returngiây thật dài dòng!):

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

Một lưu ý nghiêm trọng hơn, hãy cố gắng không trộn lẫn int, đó là một từ khóa C #, với Int32, đó là loại .NET Framework BCL - mặc dù nó hoạt động, nó chỉ làm cho mã trông lộn xộn.


3
Không hoàn toàn chắc chắn điều này sẽ thực sự chuyển thành bất cứ thứ gì hoạt động tốt hơn một khi được biên dịch
BuZz

1
Thậm chí ngắn gọn hơn trong C # 7: xóa int i;dòng và chỉ cần đi vớireturn int.TryParse (val, out var i) ? (int?) i : null;
sblom

2
Vì vậy, để hoàn thiện ;-)int? ParseNInt (string val) => int.TryParse (val, out var i) ? (int?) i : null;
Duckboy

Với C # 6, điều này có thể giảm xuống 1 dòng: return int.TryPude (value, out var result)? kết quả: (int?) null;
MeanGreen

16

Glenn Slaven : Tôi quan tâm hơn đến việc biết liệu có một phương thức khung dựng sẵn sẽ phân tích cú pháp trực tiếp thành một int nullable không?

Có cách tiếp cận này sẽ phân tích trực tiếp thành int nullable (và không chỉ int) nếu giá trị hợp lệ như null hoặc chuỗi rỗng, nhưng sẽ ném ngoại lệ cho các giá trị không hợp lệ, do đó bạn sẽ cần phải bắt ngoại lệ và trả về giá trị mặc định cho những tình huống đó:

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

Cách tiếp cận này vẫn có thể được sử dụng cho các phân tích cú pháp không null cũng như 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%");

Lưu ý: Có một phương pháp IsValid trên trình chuyển đổi mà bạn có thể sử dụng thay vì nắm bắt ngoại lệ (ngoại lệ bị ném không dẫn đến chi phí không cần thiết nếu được mong đợi). Thật không may, nó chỉ hoạt động kể từ .NET 4 nhưng vẫn có một vấn đề khi nó không kiểm tra ngôn ngữ của bạn khi xác thực các định dạng DateTime chính xác, xem lỗi 93559 .


Tôi đã thử nghiệm điều này cho các số nguyên và nó chậm hơn rất nhiều so với giá trị int.TryPude ((chuỗi), kết quả var)? kết quả: mặc định (int?);
Wouter

12
var result = int.TryParse(foo, out var f) ? f : default(int?);

Nguồn:


làm thế nào điều này có thể làm việc? Tryparse sẽ không hoạt động hoặc biến nullable và f trong ví dụ của bạn sẽ phải là null.
John Lord

Xin vui lòng bạn có thể làm rõ những gì bạn có nghĩa là @JohnLord
Jaa H

tryparse dự kiến ​​sẽ được đặt trong một biến không nullable, vì vậy, mặc định (int?) của bạn có buộc var là nullable không?
John Lord

@JohnLord có lẽ điều này sẽ giúp bạn hiểu những gì đang xảy ra với stackoverflow.com/questions/3632918/iêu
Jaa H

9

Chủ đề cũ, nhưng làm thế nào về:

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

Tôi thích điều này tốt hơn khi yêu cầu phân tích null, phiên bản TryPude sẽ không gây ra lỗi, ví dụ như ToNullableInt32 (XXX). Điều đó có thể giới thiệu các lỗi im lặng không mong muốn.


1
Đó chính xác là vấn đề - nếu chuỗi không thể được phân tích cú pháp int, chuỗi sẽ trở lại null, không ném ngoại lệ.
Svick

1
nếu giá trị không phải là số, int.Pude ném một ngoại lệ, không giống như trả về null.
một phu nhân

8

Thử cái này:

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

5

Tôi cảm thấy giải pháp của tôi là một giải pháp rất sạch sẽ và tốt đẹp:

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

Tất nhiên đây là một giải pháp chung chỉ yêu cầu đối số tổng quát có phương thức tĩnh "Parse (chuỗi)". Điều này hoạt động cho số, boolean, DateTime, vv


5

Bạn có thể quên tất cả các câu trả lời khác - có một giải pháp chung tuyệt vời: http://cleansharp.de/wordpress/2011/05/generischer-typeconverter/

Điều này cho phép bạn viết mã rất sạch như thế này:

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

và cũng:

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

1
Điều này thực sự rất hữu ích. Theo tôi, điều này nên có trong các thư viện c # tiêu chuẩn vì các chuyển đổi rất phổ biến trong mọi chương trình;)
BigChief

Điều này rất hay và hữu ích, NHƯNG tôi có thể thêm rằng nó cực kỳ chậm khi cần thực hiện chuyển đổi cho từng thành phần trong một bộ sưu tập lớn các mặt hàng. Tôi đã thử nghiệm với 20000 mặt hàng: bằng cách sử dụng phương pháp này, việc chuyển đổi 8 thuộc tính của mỗi mặt hàng mất tới 1 giờ để hoàn thành toàn bộ bộ sưu tập. Với cùng một dữ liệu mẫu nhưng sử dụng phương pháp của Matt Hamilton, chỉ mất vài giây để hoàn thành.
zed

3

Sau đây nên làm việc cho bất kỳ loại cấu trúc. Nó được dựa trên mã bởi Matt Manela từ các diễn đàn MSDN . Như Murph chỉ ra việc xử lý ngoại lệ có thể tốn kém so với việc sử dụng phương pháp TryPude dành riêng cho các loại.

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

Đây là những trường hợp thử nghiệm cơ bản tôi đã sử dụng.

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

3

Tôi sẽ đề xuất các phương thức mở rộng sau để phân tích chuỗi thành giá trị int với khả năng xác định giá trị mặc định trong trường hợp không thể phân tích cú pháp:

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

Đã có rất nhiều và thậm chí cao câu trả lời nâng cao. Bạn có thực sự nghĩ rằng câu trả lời của bạn là cần thiết và thêm chất lượng mới cho bài đăng này?
L. Guthardt

1
@ L.Guthardt Vâng, tôi nghĩ vậy. Theo tôi nghĩ câu trả lời của tôi mang lại cách phổ quát hơn để giải quyết vấn đề được mô tả trong câu hỏi. Cảm ơn bạn.
Alexanderr Neizvestnyi

2

Giải pháp này là chung chung mà không có chi phí phản ánh.

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

Tôi nghĩ bạn có thể thay thế IsNullOrEmptybằngIsNullOrWhitespace
NibblyPig 21/07/2016


1

Tôi cảm thấy tôi nên chia sẻ của tôi đó là chung chung hơn một chút.

Sử dụng:

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

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

Giải pháp:

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

Phiên bản đầu tiên chậm hơn vì nó yêu cầu thử nhưng có vẻ sạch hơn. Nếu nó không được gọi nhiều lần với các chuỗi không hợp lệ, thì điều đó không quan trọng. Nếu hiệu suất là một vấn đề, xin lưu ý rằng khi sử dụng các phương thức TryPude, bạn cần chỉ định tham số loại của ParseBy vì trình biên dịch không thể suy ra được. Tôi cũng phải xác định một đại biểu là từ khóa out không thể được sử dụng trong Func <>, nhưng ít nhất trình biên dịch thời gian này không yêu cầu một thể hiện rõ ràng.

Cuối cùng, bạn cũng có thể sử dụng nó với các cấu trúc khác, tức là thập phân, DateTime, Guid, v.v.


1

Tôi đã tìm thấy và điều chỉnh một số mã cho lớp Generic NullableParser. Mã đầy đủ có trên blog của tôi Nullable TryPude

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

1
404 không tìm thấy. nó không phải là một thực hành tốt chỉ để cung cấp một liên kết
dòng chảy bẩn

xin lỗi về điều đó @ Cập nhật luồng bẩn với mã đầy đủ. Muộn còn hơn không bao giờ :)
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;
    }

1
nếu myString không phải là số, int.Pude sẽ ném một ngoại lệ, không giống như trả về null.
một phu nhân

0

Bạn không bao giờ nên sử dụng một ngoại lệ nếu bạn không phải - chi phí quá cao.

Các biến thể trên TryPude giải quyết vấn đề - nếu bạn muốn sáng tạo (để làm cho mã của bạn trông thanh lịch hơn), bạn có thể làm điều gì đó với phương thức tiện ích mở rộng trong 3.5 nhưng mã sẽ ít nhiều giống nhau.


0

Sử dụng các đại biểu, đoạn mã sau có thể cung cấp khả năng sử dụng lại nếu bạn thấy mình cần phân tích cú pháp nullable cho nhiều loại cấu trúc. Tôi đã hiển thị cả hai phiên bản .Pude () và .TryPude () tại đây.

Đây là một ví dụ sử dụng:

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

Và đây là đoạn mã đưa bạn đến đó ...

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


0

Tôi đã đưa ra cái này, nó đã đáp ứng các yêu cầu của tôi loại nullable trong phương thức khung)

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

0

Tôi đề nghị mã dưới đây. Bạn có thể làm việc với ngoại lệ, khi xảy ra lỗi chuyển đổi.

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

Sử dụng phương thức mở rộng này trong mã (điền thuộc tính int? Tuổi của một lớp người):

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

HOẶC LÀ

AgeTextBox.Text.TryParse(i => person.Age = i, exc => { MessageBox.Show(exc.Message); });
Khi sử dụng trang web của chúng tôi, bạn xác nhận rằng bạn đã đọc và hiểu Chính sách cookieChính sách bảo mật của chúng tôi.
Licensed under cc by-sa 3.0 with attribution required.