我正在开发一个围绕TryParse的通用包装器,如下所示:
public delegate bool ParseDelegate<T>(string s, out T result);
public static T? ParseOrNull<T>(this string value, ParseDelegate<T> parse) where T : struct
{
T result;
var parsed = parse(value, out result);
return parsed ? result : (T?)null;
}
[Test]
public void ParsesValidInt()
{
Assert.AreEqual(1234, "1234".ParseOrNull<int>(int.TryParse));
}
[Test]
public void ParsesValidDecimal()
{
Assert.AreEqual(12.34M, "12.34".ParseOrNull<decimal>(decimal.TryParse));
}
这有点重复。有没有办法避免提及int.TryParse,所以我的调用如下所示:
"1234".ParseOrNull<int>()
答案 0 :(得分:6)
有没有办法避免提及int.TryParse,所以我的调用看起来如下:
不直接,因为TryParse
不是共享接口的一部分。如果存在这些值类型的共享接口,则可以通过约束来实现。
就个人而言,我不会建议使用扩展方法。我宁愿把它写成更像的东西:
public static class Parse
{
public delegate bool ParseDelegate<T>(string s, out T result);
public static T? FromString<T>(string value, ParseDelegate<T> parse) where T : struct
{
T result;
var parsed = parse(value, out result);
return parsed ? result : (T?)null;
}
public static int? ToNullableInt32(string value)
{
return FromString<int>(value, int.TryParse);
}
public static double? ToNullableDouble(string value)
{
return FromString<double>(value, double.TryParse);
}
}
这预先增加了一些开销,但允许你非常干净地写这些,即:
int? first = Parse.FromString<int>("1234", int.TryParse);
int? second = Parse.ToNullableInt32("1234");
double? third = Parse.ToNullableDouble("1234");
我认为放置扩展方法没什么价值,特别是像string
(在任何地方都使用)这样的东西,因为它“污染”了字符串本身的编译。你会在使用字符串的任何地方看到这一点 - 基本上,每当你使用这个命名空间时,你最终会在你的intellisense等中使用这些解析方法。此外,这似乎更像是一个“实用程序”而不是应该出现的东西作为字符串本身的内置功能,这就是为什么我个人更喜欢单独的类。
答案 1 :(得分:3)
简而言之,不过你可以添加一个新的辅助方法:
public static int? ParseInt(this string value)
{
return value.ParseOrNull<int>(int.TryParse);
}
然后:
"1234".ParseInt();
答案 2 :(得分:2)
了解Microsoft如何处理多种类型。它们为每种类型提供了一种方法。 Enumerable.Sum Method就是一个很好的例子。如果要简化调用代码,则应为每种类型提供重载:
public static int? ParseOrNull<int>(this string value)
{
int result;
var parsed = int.TryParse(value, out result);
return parsed ? result : (T?)null;
}
public static long? ParseOrNull<long>(this string value)
{
long result;
var parsed = long.TryParse(value, out result);
return parsed ? result : (T?)null;
}
// same for ulong, long, uint, ushort, short, byte,
// bool, float, double, decimal. Do I forget one ?
我认为简化调用比方法本身更重要。事实上,没有太多类型可以处理。
答案 3 :(得分:1)
答案是肯定的。你试图在你要转换的类型上利用静态T.TryParse(string,out T)函数的存在,我们可以通过一点点反射很容易地做到这一点。
public static T? ParseOrNull<T>(this string str)
where T: struct, IConvertible
{
// find the TryParse method.
var parseMethod = typeof(T).GetMethod("TryParse",
// We want the public static one
BindingFlags.Public | BindingFlags.Static,
Type.DefaultBinder,
// where the arguments are (string, out T)
new[] { typeof(string), typeof(T).MakeByRefType() },
null);
if (parseMethod == null)
// You need to know this so you can parse manually
throw new InvalidOperationException(
string.Format("{0} doesn't have a TryParse(..) function!",
typeof(T).FullName));
// create the parameter list for the function call
var args = new object[] { str, default(T) };
// and then call the function.
if ( (bool)parseMethod.Invoke(null, args))
return (T?)args[1]; // if it returned true
// if it returned false
return null;
}
这是我提供的原始答案,基于您需要两种不同的解析方法:一种用于值类型,另一种用于引用类型。
public delegate bool ParseDelegate<T>(string s, out T result);
public static T? ParseOrNull<T>(this string str, ParseDelegate<T> Parse)
where T: struct
{
T result;
if (!Parse(str, out result))
return null;
return result;
}
public static T ParseOrNull<T>(this string str, ParseDelegate<T> Parse)
where T : class
{
T result;
if (!Parse(str, out result))
return null;
return result;
}
答案 4 :(得分:1)
是,您可以使用Convert.ChangeType
public static T? ParseOrNull<T>(this string value) where T : struct, IConvertible
{
try
{
return (T)Convert.ChangeType(value, typeof(T));
}
catch (FormatException ex)
{
return null;
}
}
它不会像TryParse
一样具有良好的性能(使用try catch),但应该适用于所有IConvertible
类型
答案 5 :(得分:0)
public static T? ParseOrNull<T>(this string value)
where T : struct
{
T result = default(T);
object[] parameters = new object[] { value, result };
foreach (System.Reflection.MethodInfo method in
typeof(T).GetMethods()
.Where(method => method.Name == "TryParse")
.Where(method => method.GetParameters().Length == 2) //as opposed to the 4 argument version
.Take(1) //shouldn't be needed, but just in case
)
{
method.Invoke(null, parameters);
}
return (T)parameters[1];
}
正如里德所提到的,我宁愿不使用字符串的扩展方法。我只使用Parser.Parse(字符串值)。虽然容易修复,但只需删除'this'即可。