TryPude chung


196

Tôi đang cố gắng tạo một tiện ích mở rộng chung sử dụng 'TryPude' để kiểm tra xem một chuỗi có phải là một loại nhất định không:

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

điều này sẽ không được biên dịch vì nó không thể giải quyết biểu tượng 'TryPude'

Theo tôi hiểu, 'TryPude' không phải là một phần của bất kỳ giao diện nào.

Điều này có thể làm được không?

Cập nhật:

Sử dụng các câu trả lời dưới đây tôi đã đưa ra:

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

    return true;
}

Nó hoạt động khá tốt nhưng tôi nghĩ sử dụng ngoại lệ theo cách đó không cảm thấy đúng với tôi.

Cập nhật2:

Được sửa đổi để vượt qua loại thay vì sử dụng thuốc generic:

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

1
Tôi nghĩ rằng trong trường hợp chung này, bạn sẽ phải đối phó với bùn ngoại lệ. bạn có thể thêm các trường hợp để kiểm tra những thứ như ints hoặc double và sau đó sử dụng các phương thức TryPude cụ thể, nhưng bạn vẫn sẽ phải quay lại điều này để bắt các loại khác.
luke

1
Việc sử dụng chung là không cần thiết. Chỉ cần vượt qua trong Type như là một tham số. public static bool Is (đầu vào chuỗi này, Type targetType). Theo cách gọi đó, nó trông đẹp hơn một chút: x.Is (typeof (int)) -VS- x.Is <int> ()
mikeigs

2
Có một phương pháp IsValid trên trình chuyển đổi để bạn kiểm tra xem chuyển đổi có vấn đề gì không. Tôi đã sử dụng phương pháp dưới đây và dường như hoạt động tốt. 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; }
FidelXXL

@CastroXXL Cảm ơn bạn đã thể hiện sự quan tâm đến câu hỏi này, tuy nhiên phương pháp của bạn sẽ không hoạt động vì tôi muốn kiểm tra xem giá trị chuỗi có thuộc một loại nhất định chứ không phải là một đối tượng, mặc dù phương thức của bạn sẽ hữu ích cho các loại đối tượng (nhưng sẽ phải gói ConvertFrom(value)phương thức trong một try-catchkhối để bắt ngoại lệ.
Piers Myers

2
Bạn nên kiểm tra xem (targetType == null) bởi vì lần đầu tiên sử dụng nó trong mã của bạn có thể ném nhưng ngoại lệ đó sẽ bị nuốt bởi nắm bắt của bạn.
Nick Strupat

Câu trả lời:


183

Bạn nên sử dụng lớp 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
Xin lỗi để phục hồi, nhưng GetConverter có trả về null không? Tôi nghĩ rằng nếu nó đã làm thì có lẽ một ngoại lệ nên được ném thay vì về cơ bản thất bại và trả lại một cái gì đó khác. Khi tôi thử nó trên lớp của riêng tôi (trong đó tôi không định nghĩa một typeconverter), tôi đã nhận được một trình chuyển đổi từ GetConverter, nhưng sau đó ConvertFromString đã ném NotSupportedException.
user420667

3
@ user420667, tôi tin rằng bạn nên kiểm tra kết quả của CanConvertFrom (typeof (chuỗi)) trước khi thử chuyển đổi từ chuỗi. TypeConverter có thể không hỗ trợ chuyển đổi từ chuỗi.
Reuben Bond

3
Bạn có thể thêm if (typeof (T) .IsEnum) {return (T) Enum.Pude (typeof (T), input); } [như một lối tắt khá chung cho tất cả các loại Enum] trước khi nhận Trình chuyển đổi. Tôi cho rằng nó phụ thuộc vào mức độ thường xuyên bạn sẽ thực hiện các loại Enum trái ngược với các loại phức tạp hơn.
Jesse Chisholm

10
Tôi không hiểu tại sao điều này được đánh dấu là câu trả lời và được nâng cấp rất nhiều khi nó không thực hiện những gì được yêu cầu: Thử nghiệm chung chung . Mục đích chính của các phương thức TryPude là chúng không đưa ra các ngoại lệ khi cố gắng thực hiện phân tích cú pháp và có tác động thấp hơn nhiều đến hiệu suất khi phân tích thất bại và giải pháp này không cung cấp được điều đó.
Florin Dumitrescu

2
Một vấn đề với điều này là nếu T là một int và đầu vào lớn hơn int.MaxValue, nó sẽ ném System.Exception w / System.OverFlowException làm ngoại lệ bên trong. Vì vậy, nếu bạn đang mong đợi một OverflowException, bạn sẽ không nhận được nó trừ khi bạn thẩm vấn Ngoại lệ bị ném. Lý do là ConvertFromString ném OverflowException, và sau đó, cast tới T ném System.Exception.
Trevor

78

Tôi cũng yêu cầu một TryPude chung gần đây. Đây là những gì tôi nghĩ ra;

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

Sau đó, nó chỉ đơn giản là một vấn đề gọi như vậy:

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

3
Chỉ tình cờ thấy bài đăng này một lần nữa vài tháng sau đó và nhận thấy trong khi sử dụng lại nó, phương thức không thể suy ra Ttừ trình xử lý và chúng tôi phải xác định rõ ràng Tkhi chúng tôi gọi nó. Tôi tò mò, tại sao nó không thể suy ra T?
Nick Strupat

25
Tại sao bạn muốn sử dụng chức năng này? Nếu bạn biết hàm nào cần gọi để phân tích giá trị, tại sao không gọi nó trực tiếp? Nó đã biết loại đầu vào phù hợp và không cần chung chung. Giải pháp này sẽ không hoạt động đối với các loại nếu không có TryPudeHandler.
xxbbcc

2
@xxbbcc: Tôi muốn sử dụng chức năng này vì TryPude trả về một boolean cho biết liệu phân tích thành công. Nó trả về giá trị được phân tích cú pháp của bạn thông qua một tham số đầu ra. Đôi khi tôi chỉ muốn làm một cái gì đó như thế này SomeMethod(TryParse<int>(DollarTextbox.Text, int.TryParse))mà không tạo ra một biến đầu ra để bắt kết quả từ đó int.TryParse. Tuy nhiên, tôi đồng ý với tình cảm của Nick về việc có chức năng suy ra loại.
Walter Stabosz

1
Phương pháp rất hiệu quả. Rất khuyến khích.
Vladimir Kocjancic

3
Tôi muốn giới thiệu một giá trị mặc định như là một thông số thứ ba. Điều đó khắc phục vấn đề trong đó T không thể được suy ra. Ngoài ra, nó cho phép một người quyết định giá trị nào họ muốn nếu giá trị chuỗi không hợp lệ. Ví dụ: -1 có thể có nghĩa là không hợp lệ. công khai tĩnh T TryPude <T> (giá trị chuỗi, trình xử lý
TryPudeHandler

33

Sử dụng thử / bắt để kiểm soát dòng chảy là một chính sách khủng khiếp. Ném một ngoại lệ gây ra độ trễ hiệu suất trong khi thời gian chạy hoạt động xung quanh ngoại lệ. Thay vào đó xác nhận dữ liệu trước khi chuyển đổi.

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
Tôi nhận được một thông báo Resharper converter != nullluôn luôn đúng, vì vậy nó có thể bị xóa khỏi mã.
ErikE

5
@ErikE Tôi không luôn tin tưởng vào những cảnh báo ReSharper đó. Thường thì họ không thể thấy những gì xảy ra trong thời gian chạy.
ProfK

1
@ProfK MSDN không nói rằng nó có thể trả về null msdn.microsoft.com/en-us/l
danio

@danio Tôi chỉ chia sẻ kinh nghiệm của tôi với các cảnh báo R # như vậy nói chung. Tôi chắc chắn đã không ngụ ý rằng nó đã sai trong trường hợp này.
ProfK

14

Nếu bạn được thiết lập bằng cách sử dụng TryPude, bạn có thể sử dụng sự phản chiếu và làm như thế này:

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

Điều này rất tuyệt, và nó thoát khỏi những ngoại lệ mà dù sao tôi cũng không thích. Vẫn còn một chút phức tạp.
Piers Myers

6
Giải pháp hay, nhưng bất kỳ câu trả lời nào liên quan đến sự phản chiếu (đặc biệt là trong một phương thức tiện ích có thể dễ dàng được gọi từ một vòng lặp bên trong) cần có sự từ chối về hiệu suất. Xem: stackoverflow.com/questions/25458/how-costly-is-net-reflection
Patrick M

Thở dài. Vì vậy, các lựa chọn là (1) sử dụng ngoại lệ cho điều khiển luồng mã, (2) sử dụng sự phản chiếu với chi phí tốc độ của nó. Tôi đồng ý với @PiersMyer - không có sự lựa chọn nào là lý tưởng. Điều tốt là cả hai đều làm việc. :)
Jesse Chisholm

Tôi nghĩ bạn có thể thay thế Type.GetType(string.Format(...))bằng type.MakeByRefType().
Drew Noakes

3
phương thức chỉ cần được phản ánh một lần cho mỗi loại, không phải một lần cho mỗi cuộc gọi. nếu bạn biến nó thành một lớp chung với biến thành viên tĩnh, thì bạn có thể sử dụng lại đầu ra của phản xạ đầu tiên.
Andrew Hill

7

Điều này sử dụng một hàm tạo tĩnh cho từng loại chung, vì vậy nó chỉ phải thực hiện công việc đắt tiền trong lần đầu tiên bạn gọi nó trên một loại nhất định. Nó xử lý tất cả các loại trong không gian tên hệ thống có các phương thức TryPude. Nó cũng hoạt động với các phiên bản nullable của mỗi trong số đó (đó là các cấu trúc) ngoại trừ liệt kê.

    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

Còn những thứ như thế này thì sao?

http://madskristensen.net/post/Universal-data-type-checker.aspx ( Lưu trữ )

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

Điều này có thể được chuyển đổi thành một phương pháp chung khá dễ dàng.

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

Có vấn đề gì nếu bạn trả về true từ khối thử hoặc trả về false từ khối bắt? Tôi cho là không, nhưng tôi vẫn nghĩ sử dụng ngoại lệ theo cách này cảm thấy sai đối với tôi ...
Piers Myers

3
Không quan trọng bạn có trở về từ khối bắt hay không, điều này cũng tương tự. btw. Thông thường nó là xấu khi có một mệnh đề bắt chung chung : catch { }. Tuy nhiên, trong trường hợp này không có cách nào khác, vì .NET BaseNumberConverterném Exceptionlớp cơ sở trong trường hợp có lỗi chuyển đổi. Điều này rất đáng tiếc. Trong thực tế vẫn còn khá nhiều nơi là loại cơ sở này bị ném. Hy vọng Microsoft sẽ sửa những lỗi này trong phiên bản tương lai của khung.
Steven

Cảm ơn Steven, không thể nói điều đó tốt hơn.
Bob

Không sử dụng kết quả của chuyển đổi: mã là dự phòng.
BillW

4

Bạn không thể làm điều đó trên các loại chung.

Những gì bạn có thể làm là tạo một giao diện ITryParsable và sử dụng nó cho các loại tùy chỉnh thực hiện giao diện này.

Tôi đoán mặc dù bạn có ý định sử dụng điều này với các loại cơ bản như intDateTime. Bạn không thể thay đổi các loại này để thực hiện các giao diện mới.


1
Tôi tự hỏi nếu điều đó sẽ làm việc bằng cách sử dụng từ khóa động trong .net 4?
Pierre-Alain Vigete

@Pierre: Điều này sẽ không hoạt động theo mặc định trong C # với dynamictừ khóa, vì nó sẽ không hoạt động khi gõ tĩnh. Bạn có thể tạo đối tượng động của riêng bạn có thể xử lý việc này, nhưng nó không mặc định.
Steven

4

Lấy cảm hứng từ giải pháp được đăng ở đây bởi Charlie Brown, tôi đã tạo ra một TryPude chung bằng cách sử dụng sự phản chiếu, tùy ý đưa ra giá trị được phân tích cú pháp:

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

Nó có thể được gọi như vậy:

string input = "123";
decimal myDecimal;

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

Cập nhật:
Cũng nhờ giải pháp của YotaXP mà tôi thực sự thích, tôi đã tạo một phiên bản không sử dụng các phương thức mở rộng nhưng vẫn có một bản đơn, giảm thiểu nhu cầu phản ánh:

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

Gọi nó như thế này:

string input = "987";
decimal myDecimal;

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

3

Hơi muộn một chút cho bữa tiệc, nhưng đây là những gì tôi nghĩ ra. Không có ngoại lệ, phản ánh một lần (mỗi loại).

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

Lớp bổ sung là bắt buộc vì các phương thức mở rộng không được phép bên trong các lớp chung. Điều này cho phép sử dụng đơn giản, như được hiển thị bên dưới và chỉ đánh vào sự phản chiếu khi lần đầu tiên sử dụng một loại.

"5643".ParseAs<int>()

3

Đây là một lựa chọn khác.

Tôi đã viết một lớp giúp dễ dàng đăng ký bất kỳ số lượng TryParsexử lý. Nó cho phép tôi làm điều này:

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

tôi có 42 in ra bàn điều khiển.

Lớp học là:

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

Tôi thích điều này, nhưng làm thế nào bạn sẽ làm điều đó mà không có thuốc generic. Trường hợp sử dụng là sự phản ánh, tất nhiên.
Sina

Tôi đã thêm một phương thức quá tải làm một số tin tặc phản ánh. Nếu có một cách thanh lịch hơn để giải quyết nó, tôi sẽ chú ý lol gist.github.com/dasjestyr/90d8ef4dea179a6e08ddd85e0dacbc94
Trang web thẩm mỹ

2

Khi tôi muốn làm gần như điều chính xác này, tôi đã phải thực hiện nó một cách khó khăn, đưa ra sự phản ánh. Đưa ra T, suy nghĩ typeof(T)và tìm kiếm một TryParsehoặc Parsephương pháp, gọi nó nếu bạn đã tìm thấy nó.


Đây là những gì tôi sẽ đề nghị.
Steven Evers

2

Đây là cố gắng của tôi. Tôi đã làm nó như một "bài tập". Tôi đã cố gắng làm cho nó tương tự như sử dụng như các " Convert.ToX () " hiện có, v.v. Nhưng đây là phương thức mở rộng:

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

Nhược điểm chính của điều này so với TypeConverter.ConvertFrom()là lớp nguồn phải cung cấp chuyển đổi loại, điều này thường có nghĩa là bạn không thể hỗ trợ chuyển đổi sang các loại tùy chỉnh.
Ian Goldby

1

Như bạn đã nói, TryParsekhông phải là một phần của giao diện. Nó cũng không phải là thành viên của bất kỳ lớp cơ sở nào vì nó thực sự staticvà các staticchức năng không thể virtual. Vì vậy, trình biên dịch không có cách nào để đảm bảo rằng Tthực sự có một thành viên được gọi TryParse, vì vậy điều này không hoạt động.

Như @Mark đã nói, bạn có thể tạo giao diện của riêng mình và sử dụng các loại tùy chỉnh, nhưng bạn không gặp may cho các loại tích hợp.


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

Đây là một câu hỏi về "những hạn chế chung". Bởi vì bạn không có giao diện cụ thể nên bạn bị kẹt trừ khi bạn làm theo các đề xuất của câu trả lời trước.

Để biết tài liệu về điều này, hãy kiểm tra liên kết sau:

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

Nó chỉ cho bạn cách sử dụng các ràng buộc này và sẽ cung cấp cho bạn thêm một số manh mối.


0

Mượn từ http://bloss.msdn.com/b/davidebb/archive/2009/10/23/USE-c-dynamic-to-call-static-members.aspx

khi theo tham chiếu này: Làm thế nào để gọi phương thức tĩnh trong C # 4.0 với kiểu động?

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

Và sử dụng nó như sau:

  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

Tôi đã xoay sở để có được thứ gì đó hoạt động như thế này

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

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

Đây là mã của tôi

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

StaticMembersD bitWrapper được chuyển thể từ bài viết của David Ebbo (nó đang ném một AmbiguptMatchException)


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

0

Với TypeDescriptorviệc sử dụng lớp TryParsetheo cách liên quan:

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

Mặc dù mã này có thể giải quyết câu hỏi, bao gồm giải thích về cách thức và lý do giải quyết vấn đề này thực sự sẽ giúp cải thiện chất lượng bài đăng của bạn và có thể dẫn đến nhiều lượt bình chọn hơn. Hãy nhớ rằng bạn đang trả lời câu hỏi cho độc giả trong tương lai, không chỉ người hỏi bây giờ. Vui lòng chỉnh sửa câu trả lời của bạn để thêm giải thích và đưa ra dấu hiệu về những hạn chế và giả định được áp dụng.
tiếng bíp đôi

0

Sử dụng thông tin trên, đây là những gì tôi đã phát triển. Nó sẽ chuyển đổi đối tượng trực tiếp là có thể, nếu không nó sẽ chuyển đổi đối tượng thành một chuỗi và gọi phương thức TryPude cho loại đối tượng mong muốn.

Tôi lưu trữ các phương thức trong một từ điển vì mỗi phương thức đều gặp phải để giảm tải phương thức.

Có thể kiểm tra xem đối tượng có thể được chuyển đổi trực tiếp thành loại mục tiêu hay không, điều này sẽ làm giảm thêm phần chuyển đổi chuỗi. Nhưng tôi sẽ bỏ nó ngay bây giờ.

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

Tôi đã phải thêm một chức năng khác để hỗ trợ liệt kê. Có vẻ như phân tích Enum yêu cầu thuộc tính "where T: struct" và tôi muốn nó hoạt động trên mọi thứ có thể chuyển đổi. (có lẽ nên thêm một thuộc tính chuyển đổi cho loại). Tuy nhiên, một số gợi ý sau có vẻ đơn giản hơn (do đó tốt hơn).
B Duffy

0

Tôi đặt một loạt các ý tưởng ở đây cùng nhau và kết thúc với một giải pháp rất ngắn.

Đây là một phương thức mở rộng trên một chuỗi

enter code here

Tôi đã thực hiện nó với cùng một dấu chân như các phương thức TryPude trên các kiểu số

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

    }

'' '


test testValue = 0; if ("1234" .TryPude <float> (out testValue)) {doS Somethingood (); } other {handleTheBadness (); }
JD Hicks

0

T.TryPude ... tại sao?

Tôi thấy không có lợi ích trong việc có TryParsechức năng chung như vậy . Có quá nhiều chiến lược khác nhau để phân tích cú pháp và chuyển đổi dữ liệu giữa các loại khác nhau, với hành vi có thể xung đột. Làm thế nào chức năng này có thể biết chiến lược nào để chọn theo kiểu không ngữ cảnh?

  • các lớp có chức năng TryPude chuyên dụng có thể được gọi
  • các lớp có chức năng Parse chuyên dụng có thể được gói bằng kết quả thử và bool
  • các lớp có quá tải toán tử, làm thế nào bạn để họ xử lý phân tích cú pháp?
  • loại mô tả được tích hợp sử dụng Convert.ChangeType. API này có thể tùy chỉnh trong thời gian chạy. Chức năng của bạn có yêu cầu hành vi mặc định hoặc cho phép tùy chỉnh không?
  • bạn có nên cho phép bất kỳ khung ánh xạ nào cố gắng phân tích cú pháp cho bạn không?
  • Làm thế nào bạn sẽ xử lý xung đột ở trên?

-2

Một phiên bản để nhận con cháu từ 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;
    }
}
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.