我试图创建一个通用扩展,使用'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;
}
}
利用上面的信息,这就是我开发的东西。它将直接转换对象是可能的,否则它将把对象转换为字符串并调用所需对象类型的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;
}
利用上面的信息,这就是我开发的东西。它将直接转换对象是可能的,否则它将把对象转换为字符串并调用所需对象类型的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;
}
我使用Charlie Brown的答案作为Json.NET的JObject扩展方法。
public static class JObjectExtension
{
public delegate bool TryParseHandler<T>(string value, out T result);
public static bool TryParsePropertyAs<T>(
this JObject jObject,
string propertyName,
TryParseHandler<T> parser,
out T value)
{
value = default;
if (!jObject.TryGetValue(propertyName, out var jToken)) return false;
if (!parser(jToken.Value<string>(), out T result)) return false;
value = result;
return true;
}
}
有点晚了,但这是我想到的。无异常,一次性(每种类型)反射。
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>()
我把一堆想法放在一起,最终得到了一个非常简短的解决方案。
这是一个字符串的扩展方法
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);
}
'''