일반적인 TryParse


196

'TryParse'를 사용하여 문자열이 주어진 유형인지 확인하는 일반 확장을 만들려고합니다.

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

심볼 'TryParse'를 해결할 수 없으므로 컴파일되지 않습니다.

내가 이해하는 것처럼 'TryParse'는 인터페이스의 일부가 아닙니다.

이것이 전혀 가능합니까?

최신 정보:

아래 답변을 사용하여 생각해 냈습니다.

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

    return true;
}

그것은 잘 작동하지만 예외를 사용하는 것이 나에게 옳지 않다고 생각합니다.

업데이트 2 :

제네릭을 사용하지 않고 형식을 전달하도록 수정했습니다.

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

1
나는이 일반적인 경우에는 예외 kludge를 처리해야한다고 생각합니다. int 또는 double과 같은 것을 확인하기 위해 사례를 추가 한 다음 특정 TryParse 메서드를 사용할 수 있지만 다른 유형을 잡으려면 여전히이 문제로 넘어 가야합니다.
luke

1
제네릭을 사용할 필요는 없습니다. Type을 매개 변수로 전달하십시오. public static bool Is (이 문자열 입력, 유형 targetType). 그렇게 호출하면 조금 더 예쁘게 보입니다. x.Is (typeof (int)) -VS- x.Is <int> ()
mikesigs

2
변환기에 IsValid 메서드가있어 변환에 문제가 있는지 확인할 수 있습니다. 아래 방법을 사용하여 제대로 작동하는 것 같습니다. 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이 질문에 관심을 가져 주셔서 감사합니다.하지만 문자열 값이 객체가 아닌 특정 유형인지 여부를 확인하고 싶었으므로 메서드가 제대로 작동하지 않습니다. 예외를 잡기 ConvertFrom(value)try-catch
Piers Myers

2
코드에서 처음 사용할 때 (targetType == null) 여부를 확인해야하지만 예외는 catch에 의해 삼킬 수 있습니다.
Nick Strupat

답변:


183

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
부활하여 죄송하지만 GetConverter가 null을 반환합니까? 나는 그것이 아마도 자동적으로 실패하고 다른 것을 반환하는 대신 예외를 던져야한다고 생각합니다. 타입 변환기를 정의하지 않은 자체 클래스에서 시도했을 때 GetConverter에서 변환기를 얻었지만 ConvertFromString에서 NotSupportedException이 발생했습니다.
user420667

3
@ user420667, 문자열에서 변환을 시도하기 전에 CanConvertFrom (typeof (string))의 결과를 확인해야한다고 생각합니다. TypeConverter는 문자열 변환을 지원하지 않을 수 있습니다.
Reuben Bond

3
당신은 추가 할 수있는 경우 (대해서 typeof (T) .IsEnum) {수익률 (T) Enum.Parse (대해서 typeof (T), 입력); } 변환기를 사용하기 전에 [모든 Enum 유형의 일반적인 단축키로]. 더 복잡한 유형이 아닌 Enum 유형을 얼마나 자주 수행 할 것인지에 달려 있다고 가정합니다.
Jesse Chisholm 23시

10
왜 이것이 답변으로 표시되고 요청 된 것을 구현하지 않을 때 너무 많이 투표되었는지 이해하지 못합니다 : 일반적인 Try Parse. TryParse 메서드의 주요 목적은 구문 분석을 시도 할 때 예외를 발생시키지 않으며 구문 분석이 실패하고이 솔루션이이를 제공하지 못하는 경우 성능에 훨씬 적은 영향을 미치도록하는 것입니다.
Florin Dumitrescu

2
이것에 대한 한 가지 문제는 T가 int이고 입력이 int.MaxValue보다 크면 System.Exception w / System.Exception을 내부 예외로 throw한다는 것입니다. 따라서 OverflowException이 예상되는 경우 throw 된 예외를 조사하지 않으면 예외가 발생하지 않습니다. 그 이유는 ConvertFromString이 OverflowException을 throw 한 다음 T로 캐스트하면 System.Exception이 발생하기 때문입니다.
Trevor

78

최근에 일반적인 TryParse가 필요했습니다. 여기에 내가 생각해 낸 것이 있습니다.

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

그런 다음 단순히 이렇게 부르는 것입니다.

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

3
방금이 게시물을 몇 달 후에 다시 찾아 와서 다시 사용하는 동안 메서드가 T처리기에서 유추 할 수 없으며 T호출 할 때 명시 적으로 지정해야 한다는 것을 알았 습니다. 궁금해서 왜 추론 할 수 T없습니까?
Nick Strupat

25
왜이 기능을 사용하고 싶습니까? 값을 구문 분석하기 위해 어떤 함수를 호출해야하는지 알고 있다면 직접 호출하지 않겠습니까? 이미 올바른 입력 유형을 알고 있으며 제네릭이 필요하지 않습니다.이 솔루션은 TryParseHandler가없는 유형에서는 작동하지 않습니다.
xxbbcc

2
@ xxbbcc : TryParse가 구문 분석이 성공했는지 나타내는 부울을 반환하기 때문에이 함수를 사용하고 싶습니다. 출력 매개 변수를 통해 구문 분석 된 값을 반환합니다. 때로는 SomeMethod(TryParse<int>(DollarTextbox.Text, int.TryParse))결과를 잡기 위해 출력 변수를 만들지 않고 이와 같은 것을하고 싶습니다 int.TryParse. 그러나 함수의 유형을 유추하는 것에 대한 Nick의 감정에 동의합니다.
Walter Stabosz 2014 년

1
매우 효율적인 방법. 추천.
Vladimir Kocjancic

3
세 번째 매개 변수로 기본값을 권장합니다. 이는 T를 유추 할 수없는 문제를 해결합니다. 또한 문자열 값이 유효하지 않은 경우 원하는 값을 결정할 수 있습니다. 예를 들어, -1은 유효하지 않을 수 있습니다. 공개 정적 T TryParse <T> (문자열 값, TryParseHandler <T> 핸들러, T defaultValue)
Rhyous

33

흐름 제어에 try / catch를 사용하는 것은 끔찍한 정책입니다. 예외를 throw하면 런타임이 예외를 해결하는 동안 성능이 저하됩니다. 대신 변환하기 전에 데이터의 유효성을 검사하십시오.

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
converter != null항상 사실 인 Resharper 통지를 받고 있으므로 코드에서 제거 할 수 있습니다.
ErikE

5
@ErikE 나는 그 ReSharper 경고를 항상 신뢰하지는 않습니다. 종종 그들은 런타임에 무슨 일이 일어나는지 알 수 없습니다.
ProfK


@ danio 나는 일반적인 R # 경고와 함께 내 경험을 공유하고있었습니다. 나는이 경우에 그것이 틀렸다는 것을 암시하지 않았다.
ProfK

14

TryParse를 사용하도록 설정 한 경우 리플렉션을 사용하여 다음과 같이 수행 할 수 있습니다.

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

이것은 매우 시원하며, 어쨌든 내가 싫어했던 예외를 제거합니다. 그래도 여전히 조금 복잡했습니다.
Piers Myers

6
좋은 해결책이지만 리플렉션과 관련된 답변 (특히 내부 루프에서 쉽게 호출 할 수있는 유틸리티 방법)에는 성능에 대한 면책이 필요합니다. 참조 : stackoverflow.com/questions/25458/how-costly-is-net-reflection
Patrick M

한숨. 따라서 (1) 코드 흐름 제어에 예외를 사용하고 (2) 속도 비용과 함께 리플렉션을 사용하십시오. @PiersMyers에 동의합니다. 어느 쪽도 이상적이지 않습니다. 그들은 둘 다 작동합니다. :)
Jesse Chisholm 22.56에

난 당신이를 대체 할 수 있다고 생각 Type.GetType(string.Format(...))과 함께 type.MakeByRefType().
Drew Noakes

3
메소드는 호출 당 한 번이 아니라 유형별로 한 번만 반영하면됩니다. 정적 멤버 변수를 사용하여이 클래스를 일반 클래스로 만들면 첫 번째 리플렉션의 출력을 재사용 할 수 있습니다.
앤드류 힐

7

이것은 각 제네릭 형식에 대해 정적 생성자를 사용하므로 주어진 형식에서 처음 호출 할 때 값 비싼 작업 만 수행하면됩니다. TryParse 메소드가있는 시스템 네임 스페이스의 모든 유형을 처리합니다. 열거 형을 제외하고 각 구조체 (구조체)의 nullable 버전에서도 작동합니다.

    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

이런 건 어때?

http://madskristensen.net/post/Universal-data-type-checker.aspx ( 아카이브 )

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

이것은 일반적인 방법으로 쉽게 변환 할 수 있습니다.

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

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

try 블록에서 true를 반환하는지 catch 블록에서 false를 반환하는지는 중요합니까? 나는 그렇지 않다고 생각하지만 여전히 이런 식으로 예외를 사용하는 것이 나에게 잘못이라고 생각합니다 ...
Piers Myers

3
catch 블록에서 돌아 왔는지 여부는 중요하지 않습니다. btw. 일반적으로 일반적인 catch 절을 갖는 것은 좋지 않습니다 : catch { }. 그러나이 경우 변환 오류가 발생할 경우 .NET BaseNumberConverter에서 Exception기본 클래스를 throw 하므로 대안이 없습니다 . 이것은 매우 불행합니다. 사실이 기본 유형이 던져진 곳이 여전히 꽤 있습니다. Microsoft가 향후 버전의 프레임 워크에서이를 수정하기를 바랍니다.
Steven

고마워 Steven, 더 잘 말하지 못했습니다.
Bob

변환 결과를 사용하지 않음 : 코드가 중복됩니다.
BillW

4

일반적인 유형에서는 할 수 없습니다.

당신이 할 수있는 일은 ITryParsable 인터페이스를 만들고이 인터페이스를 구현하는 사용자 정의 유형에 사용하는 것입니다.

나는 당신이 intand과 같은 기본 유형으로 이것을 사용하려고한다고 생각합니다 DateTime. 새 인터페이스를 구현하기 위해 이러한 유형을 변경할 수 없습니다.


1
.net 4에서 동적 키워드를 사용하여 작동하는지 궁금합니다.
Pierre-Alain Vigeant 2016 년

@Pierre : C #에서는 dynamic키워드를 사용하여 기본적으로 작동하지 않습니다. 정적 입력에서는 작동하지 않기 때문입니다. 이를 처리 할 수있는 고유 한 동적 객체를 만들 수 있지만 기본값은 아닙니다.
Steven

4

Charlie Brown이 게시 한 솔루션에서 영감을 얻어 선택적으로 구문 분석 된 값을 출력하는 리플렉션을 사용하여 일반적인 TryParse를 만들었습니다.

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

다음과 같이 호출 할 수 있습니다.

string input = "123";
decimal myDecimal;

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

업데이트 :
또한 내가 좋아하는 YotaXP 솔루션 덕분에 확장 방법을 사용하지 않지만 여전히 싱글 톤이있는 버전을 작성하여 반영해야 할 필요성을 최소화했습니다.

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

다음과 같이 호출하십시오.

string input = "987";
decimal myDecimal;

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

3

파티에 꽤 늦었지만 여기에 내가 생각해 낸 것이 있습니다. 예외는 없으며 한 번 (유형별) 반영입니다.

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

확장 클래스는 일반 클래스 내에서 허용되지 않으므로 추가 클래스가 필요합니다. 이를 통해 아래와 같이 간단한 사용법을 사용할 수 있으며 유형을 처음 사용할 때만 반영됩니다.

"5643".ParseAs<int>()

3

다른 옵션이 있습니다.

여러 TryParse핸들러를 쉽게 등록 할 수있는 클래스를 작성했습니다 . 이 작업을 수행 할 수 있습니다.

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

내가받을 42콘솔에 인쇄.

수업은 다음과 같습니다

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

나는 이것을 좋아하지만 제네릭없이 어떻게 할 것입니까? 물론 유스 케이스는 반영입니다.
Sinaesthetic

리플렉션 해커를 수행하는 오버로드 된 메소드를 추가했습니다. 좀 더 우아한 방법이 있다면, 나는 모든 눈입니다 lol gist.github.com/dasjestyr/90d8ef4dea179a6e08ddd85e0dacbc94
Sinaesthetic

2

내가 거의이 정확한 일을하고 싶을 때, 나는 그것을 반영하여 어려운 방법으로 구현해야했다. 주어진 T, 또는를 찾은 후 호출 typeof(T)하여 TryParse또는 Parse메소드를 찾아보십시오.


이것이 내가 제안하려고하는 것입니다.
Steven Evers 2016 년

2

이것은 나의 시도이다. 나는 그것을 "운동"으로했다. 나는 기존의 " Convert.ToX () "-ones 처럼 사용하기 위해 비슷하게 만들려고 노력했지만 이것은 확장 방법입니다.

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

에 비해이의 가장 큰 단점은 TypeConverter.ConvertFrom()이다 소스 클래스는 일반적으로 사용자 정의 유형 변환을 지원 할 수 없음을 의미 유형 변환을 제공한다.
이안 Goldby

1

당신이 말했듯이, TryParse인터페이스의 일부가 아닙니다. 또한 실제로 기본 static이고 static함수가 될 수 없으므로 주어진 기본 클래스의 멤버가 아닙니다 virtual. 따라서 컴파일러에는 T실제로이라는 멤버가 있는지 확인할 방법 TryParse이 없으므로 작동하지 않습니다.

@Mark가 말했듯이 자체 인터페이스를 만들고 사용자 정의 유형을 사용할 수 있지만 내장 유형에 대해서는 운이 좋지 않습니다.


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

이것은 '일반적인 제약'의 문제입니다. 특정 인터페이스가 없기 때문에 이전 답변의 제안을 따르지 않으면 멈춰 있습니다.

이에 대한 설명서는 다음 링크를 확인하십시오.

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

이러한 제약 조건을 사용하는 방법을 보여 주므로 더 많은 단서를 제공해야합니다.


0

에서 빌린 http://blogs.msdn.com/b/davidebb/archive/2009/10/23/using-c-dynamic-to-call-static-members.aspx

이 참조를 따르는 경우 : 동적 유형으로 C # 4.0에서 정적 메소드를 호출하는 방법은 무엇입니까?

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

다음과 같이 사용하십시오.

  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

나는 이런 식으로 작동하는 것을 얻었습니다.

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

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

여기 내 코드가 있습니다

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

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


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

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

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

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

            var method = methods.FirstOrDefault();

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

                return false;
            }

            result = method.Invoke( null, args );

            return true;
        }
    }

StaticMembersDynamicWrapper는 David Ebbo의 기사 에서 수정되었습니다 (AmbiguousMatchException 발생)


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

0

TypeDescriptor클래스 사용 TryParse관련 방법 :

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

이 코드가 문제를 해결하는 방법과 이유에 대한 설명포함 하여 질문을 해결할 수는 있지만 게시물의 품질을 향상시키는 데 도움이되며 더 많은 투표를 할 수 있습니다. 지금 질문하는 사람 만이 아니라 앞으로 독자들에게 질문에 대답하고 있음을 기억하십시오. 제발 편집 설명을 추가하고 제한 및 가정이 적용 무엇의 표시를 제공하는 답변을.
double-beep

0

위의 정보를 사용하여 이것이 내가 개발 한 것입니다. 개체를 직접 변환 할 수 있습니다. 그렇지 않으면 개체를 문자열로 변환하고 원하는 개체 유형에 대해 TryParse 메서드를 호출합니다.

메소드 페치로드를 줄이기 위해 각각의 메소드를 사전에 캐시합니다.

객체를 대상 유형으로 직접 변환 할 수 있는지 테스트하여 문자열 변환 부분을 더 줄일 수 있습니다. 하지만 지금은 그만 두겠습니다.

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

열거를 지원하기 위해 다른 함수를 추가해야했습니다. Enum 구문 분석에는 "where T : struct"속성이 필요하고 이것이 컨버터블에서 작동하기를 원합니다. (아마도 유형에 변환 가능한 속성을 추가해야합니다). 그러나 다음 제안 중 일부는 더 단순 해 보입니다.
B Duffy

0

나는 여기에 많은 아이디어를 모았고 매우 짧은 해결책으로 끝났습니다.

이것은 문자열의 확장 방법입니다

enter code here

숫자 유형의 TryParse 메소드와 동일한 발자국으로 만들었습니다.

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

0

T.TryParse ... 왜?

나는 그러한 일반적인 TryParse기능 을 갖는 데 아무런 이점이 없다고 생각 합니다. 상충되는 동작으로 서로 다른 유형간에 데이터를 구문 분석하고 변환하기위한 전략이 너무 많습니다. 이 함수는 컨텍스트없는 방식으로 어떤 전략을 선택해야하는지 어떻게 알 수 있습니까?

  • 전용 TryParse 함수가있는 클래스를 호출 할 수 있습니다.
  • 전용 구문 분석 기능이있는 클래스는 try-catch 및 bool 결과로 래핑 될 수 있습니다.
  • 연산자 오버로드가있는 클래스, 구문 분석을 어떻게 처리 할 수 ​​있습니까?
  • 형식 설명자는을 사용하여 기본 제공됩니다 Convert.ChangeType. 이 API는 런타임에 사용자 정의 할 수 있습니다. 함수에 기본 동작이 필요합니까? 아니면 사용자 정의가 가능합니까?
  • 매핑 프레임 워크가 구문 분석을 시도하도록 허용해야합니까?
  • 위의 충돌을 어떻게 처리 하시겠습니까?

-2

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;
    }
}
당사 사이트를 사용함과 동시에 당사의 쿠키 정책개인정보 보호정책을 읽고 이해하였음을 인정하는 것으로 간주합니다.
Licensed under cc by-sa 3.0 with attribution required.