Generic TryParse
Próbuję stworzyć ogólne rozszerzenie, które używa 'TryParse', aby sprawdzić, czy łańcuch jest danym typem:
public static bool Is<T>(this string input)
{
T notUsed;
return T.TryParse(input, out notUsed);
}
Nie będzie kompilacji, ponieważ nie może rozwiązać symbolu 'TryParse'
Jak rozumiem, 'TryParse' nie jest częścią żadnego interfejsu.
Czy to w ogóle możliwe?Update:
Korzystając z poniższych odpowiedzi, które wymyśliłem:
public static bool Is<T>(this string input)
{
try
{
TypeDescriptor.GetConverter(typeof(T)).ConvertFromString(input);
}
catch
{
return false;
}
return true;
}
To działa całkiem dobrze, ale myślę, że używanie WYJĄTKÓW w ten sposób nie czuje się dobrze ja.
Update2:
Zmodyfikowano, aby przekazać Typ zamiast używać generyków:
public static bool Is(this string input, Type targetType)
{
try
{
TypeDescriptor.GetConverter(targetType).ConvertFromString(input);
return true;
}
catch
{
return false;
}
}
18 answers
Powinieneś użyć klasy 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);
}
}
Warning: date(): Invalid date.timezone value 'Europe/Kyiv', we selected the timezone 'UTC' for now. in /var/www/agent_stack/data/www/doraprojects.net/template/agent.layouts/content.php on line 54
2017-01-09 07:57:44
Ostatnio wymagałem też ogólnego Tryparsu. Oto co wymyśliłem;
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);
W takim razie wystarczy zadzwonić:
var value = TryParse<int>("123", int.TryParse);
var value2 = TryParse<decimal>("123.123", decimal.TryParse);
Warning: date(): Invalid date.timezone value 'Europe/Kyiv', we selected the timezone 'UTC' for now. in /var/www/agent_stack/data/www/doraprojects.net/template/agent.layouts/content.php on line 54
2011-07-01 22:11:03
Używanie TRY / catches do kontroli przepływu jest straszną Polityką. Wyrzucenie wyjątku powoduje opóźnienie wydajności, podczas gdy runtime działa wokół wyjątku. Zamiast tego zweryfikuj dane przed konwersją.
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);
Warning: date(): Invalid date.timezone value 'Europe/Kyiv', we selected the timezone 'UTC' for now. in /var/www/agent_stack/data/www/doraprojects.net/template/agent.layouts/content.php on line 54
2012-04-11 20:30:52
Jeśli używasz TryParse, możesz użyć reflection i zrobić to w następujący sposób:
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});
}
Warning: date(): Invalid date.timezone value 'Europe/Kyiv', we selected the timezone 'UTC' for now. in /var/www/agent_stack/data/www/doraprojects.net/template/agent.layouts/content.php on line 54
2010-06-02 22:00:12
To używa statycznego konstruktora dla każdego typu generycznego, więc musi wykonać kosztowną pracę tylko przy pierwszym wywołaniu go na danym typie. Obsługuje WSZYSTKIE TYPY w przestrzeni nazw systemu, które mają metody TryParse. Działa również z wersjami nullable każdej z tych (które są strukturami) z wyjątkiem wyliczeń.
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;
}
}
Warning: date(): Invalid date.timezone value 'Europe/Kyiv', we selected the timezone 'UTC' for now. in /var/www/agent_stack/data/www/doraprojects.net/template/agent.layouts/content.php on line 54
2013-05-29 17:03:25
Nie możesz tego robić na typach ogólnych.
Co można zrobić, to stworzyć interfejs ITryParsable i używać go dla niestandardowych typów, które implementują ten interfejs.
Myślę jednak, że zamierzasz używać tego z podstawowymi typami, takimi jak int
i DateTime
. Nie można zmienić tych typów, aby zaimplementować nowe interfejsy.
Warning: date(): Invalid date.timezone value 'Europe/Kyiv', we selected the timezone 'UTC' for now. in /var/www/agent_stack/data/www/doraprojects.net/template/agent.layouts/content.php on line 54
2010-06-02 21:23:50
Zainspirowany rozwiązaniem zamieszczonym tutaj przez Charliego Browna, stworzyłem ogólny TryParse używając reflection, który opcjonalnie wypisuje parsowaną wartość:
/// <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;
}
Można go nazwać tak:
string input = "123";
decimal myDecimal;
bool myIntSuccess = TryParse<int>(input);
bool myDecimalSuccess = TryParse<decimal>(input, out myDecimal);
Update:
Również dzięki rozwiązaniu YotaXP, które bardzo mi się podoba, stworzyłem wersję, która nie używa metod rozszerzeń, ale nadal ma singleton, minimalizując potrzebę wykonywania refleksji: {]}
/// <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);
}
}
Nazwij to tak:
string input = "987";
decimal myDecimal;
bool myIntSuccess = TryParseHelper<int>.TryParse(input);
bool myDecimalSuccess = TryParseHelper<decimal>.TryParse(input, out myDecimal);
Warning: date(): Invalid date.timezone value 'Europe/Kyiv', we selected the timezone 'UTC' for now. in /var/www/agent_stack/data/www/doraprojects.net/template/agent.layouts/content.php on line 54
2014-09-29 13:04:49
Może coś takiego?
Http://madskristensen.net/post/Universal-data-type-checker.aspx (Archiwum )
/// <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;
}
To może być przekonwertowane do metody generycznej dość łatwo.
public static T Is<T>(this string input)
{
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;
}
Warning: date(): Invalid date.timezone value 'Europe/Kyiv', we selected the timezone 'UTC' for now. in /var/www/agent_stack/data/www/doraprojects.net/template/agent.layouts/content.php on line 54
2015-07-24 14:42:52
Kiedy chciałem zrobić prawie dokładnie to, musiałem to zaimplementować w trudny sposób, biorąc pod uwagę refleksję. Biorąc pod uwagę T
, zastanów się nad typeof(T)
i poszukaj metody TryParse
lub Parse
, wywołując ją, jeśli ją znalazłeś.
Warning: date(): Invalid date.timezone value 'Europe/Kyiv', we selected the timezone 'UTC' for now. in /var/www/agent_stack/data/www/doraprojects.net/template/agent.layouts/content.php on line 54
2010-06-02 21:26:37
To moja próba. Zrobiłem to jako "ćwiczenie". Starałem się upodobnić go do istniejącego " Convert.ToX () " - ones etc. Ale to jest metoda rozszerzenia:
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; }
}
Warning: date(): Invalid date.timezone value 'Europe/Kyiv', we selected the timezone 'UTC' for now. in /var/www/agent_stack/data/www/doraprojects.net/template/agent.layouts/content.php on line 54
2014-05-21 13:13:37
Trochę późno na imprezę, ale oto co wymyśliłem. Bez wyjątków, jednorazowe (na typ) odbicie.
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;
}
}
}
}
Dodatkowa klasa jest wymagana, ponieważ metody rozszerzania nie są dozwolone wewnątrz klas ogólnych. Pozwala to na proste użycie, jak pokazano poniżej, i uderza w odbicie tylko przy pierwszym użyciu typu.
"5643".ParseAs<int>()
Warning: date(): Invalid date.timezone value 'Europe/Kyiv', we selected the timezone 'UTC' for now. in /var/www/agent_stack/data/www/doraprojects.net/template/agent.layouts/content.php on line 54
2014-08-08 13:58:41
Oto inna opcja.
Napisałem klasę, która ułatwia rejestrację dowolnej liczby TryParse
obsługi. Pozwala mi to zrobić:
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);
};
Dostaję 42
wydrukowane na konsoli.
Klasa to:
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);
}
}
Warning: date(): Invalid date.timezone value 'Europe/Kyiv', we selected the timezone 'UTC' for now. in /var/www/agent_stack/data/www/doraprojects.net/template/agent.layouts/content.php on line 54
2016-02-26 02:37:14
Jak powiedziałeś, TryParse
nie jest częścią interfejsu. Nie jest również członkiem żadnej z klas bazowych, ponieważ w rzeczywistości jest to static
i static
funkcje nie mogą być virtual
. Tak więc kompilator nie ma możliwości zapewnienia, że T
faktycznie ma członka o nazwie TryParse
, więc to nie działa.
Jak powiedział @ Mark, możesz stworzyć własny interfejs i używać niestandardowych typów, ale nie masz szczęścia do wbudowanych typów.
Warning: date(): Invalid date.timezone value 'Europe/Kyiv', we selected the timezone 'UTC' for now. in /var/www/agent_stack/data/www/doraprojects.net/template/agent.layouts/content.php on line 54
2010-06-02 21:25:04
Jest to kwestia "ogólnych ograniczeń". Ponieważ nie masz określonego interfejsu, utknąłeś, chyba że zastosujesz się do sugestii poprzedniej odpowiedzi.
Aby uzyskać dokumentację na ten temat, sprawdź poniższy link:
Http://msdn.microsoft.com/en-us/library/ms379564 (VS.80). aspx
Pokazuje, jak korzystać z tych ograniczeń i powinien dać więcej wskazówek.
Warning: date(): Invalid date.timezone value 'Europe/Kyiv', we selected the timezone 'UTC' for now. in /var/www/agent_stack/data/www/doraprojects.net/template/agent.layouts/content.php on line 54
2010-06-02 21:22:33
Zapożyczone z http://blogs.msdn.com/b/davidebb/archive/2009/10/23/using-c-dynamic-to-call-static-members.aspx
Po wykonaniu tego odniesienia: Jak wywołać metodę statyczną w C#4.0 z typem dynamicznym?
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;
}
}
}
I użyj go w następujący sposób:
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;
}
Warning: date(): Invalid date.timezone value 'Europe/Kyiv', we selected the timezone 'UTC' for now. in /var/www/agent_stack/data/www/doraprojects.net/template/agent.layouts/content.php on line 54
2017-05-23 10:31:31
Udało mi się zdobyć coś, co działa tak
var result = "44".TryParse<int>();
Console.WriteLine( "type={0}, value={1}, valid={2}",
result.Value.GetType(), result.Value, result.IsValid );
Oto Mój kod
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;
}
}
[2]} StaticMembersDynamicWrapper jest zaadaptowany z artykułu Davida Ebbo (rzucał niejednoznaczną Matchexception)public static T Get<T>(string val)
{
return (T) TypeDescriptor.GetConverter(typeof (T)).ConvertFromInvariantString(val);
}
Warning: date(): Invalid date.timezone value 'Europe/Kyiv', we selected the timezone 'UTC' for now. in /var/www/agent_stack/data/www/doraprojects.net/template/agent.layouts/content.php on line 54
2013-10-29 12:23:26
Wersja do pobierania potomków z 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;
}
}
Warning: date(): Invalid date.timezone value 'Europe/Kyiv', we selected the timezone 'UTC' for now. in /var/www/agent_stack/data/www/doraprojects.net/template/agent.layouts/content.php on line 54
2013-10-01 17:47:11