我正在使用以下方法提供对各种Type类的TryParse()方法的快速内联访问。基本上我希望能够在可能的情况下解析来自Web服务的字符串,否则返回默认值。
private Int64 Int64Parse(string value) {
Int64 result;
if (!Int64.TryParse(value, out result)) { return default(Int64); }
return result;
}
private DateTime DateTimeParse(string value) {
DateTime result;
if (!DateTime.TryParse(value, out result)) { return default(DateTime); }
return result;
}
private Decimal DecimalParse(string value) {
Decimal result;
if (!Decimal.TryParse(value, out result)) { return default(Decimal); }
return result;
}
这些是非常重复的,对我来说,可能有办法将它们包装成一个通用的方法。
我遇到以下情况,但不确定如何继续或如何搜索如何继续。
private T ParseString<T>(string value) {
T result;
if (!T.TryParse(value, out result)) { return default(T); }
return result;
}
任何帮助将不胜感激。 感谢。
==编辑== 添加一些上下文。这是为了收听来自特定信用卡结算公司的回发的听众。我没有在此步骤进行验证,因为这是在稍后的业务规则步骤中完成的。例如,我不在乎bank_batch_number是作为int,string还是冻干的啮齿动物进入;如果我不能干净地记录我不使用的字段,我不会停止异常。我关心ext_product_id存在于我们的数据库中,并且在消息中具有与currency_amount_settled相匹配的价格;如果该测试失败,则交易被暂停,将记录警告,我们的CS员工和我自己将收到警报。
下面提到的文化事物是圣人的建议。
答案 0 :(得分:7)
不,这些方法基本上是完全分开的 - 编译器并不知道DateTime.TryParse
与Int64.TryParse
等有任何相似之处。
你可以创建一个Dictionary<Type, Delegate>
来映射从目标类型到方法,然后编写如下内容:
private delegate bool Parser<T>(string value, out T result);
private T Parse<T>(string value) where T : struct
{
// TODO: Validate that typeof(T) is in the dictionary
Parser<T> parser = (Parser<T>) parsers[typeof(T)];
T result;
parser(value, out result);
return result;
}
您可以像这样填写字典:
static readonly Dictionary<Type, Delegate> Parsers = CreateParsers();
static Dictionary<Type, Delegate> CreateParsers()
{
var parsers = new Dictionary<Type, Delegate>();
AddParser<DateTime>(parsers, DateTime.TryParse);
AddParser<Int64>(parsers, Int64.TryParse);
return parsers;
}
static void AddParser<T>(Dictionary<Type, Delegate> parsers, Parser<T> parser)
{
parsers[typeof(T)] = parser;
}
请注意,TryParse
模式指出out
参数的值无论如何都是该类型的默认值,因此您不需要条件逻辑。这意味着即使你的重复方法也会变得更简单:
private static Decimal DecimalParse(string value) {
Decimal result;
Decimal.TryParse(value, out result);
return result;
}
另外,请注意默认情况下TryParse
模式将使用该线程的当前文化。如果这是用于解析来自Web服务的传入数据,我强烈建议您使用不变文化。 (我个人也不会默默地忽略不良数据,但我认为这是故意的。)
答案 1 :(得分:2)
public delegate bool TryParseDelegate<T>(string str, out T value);
public static T ParseOrDefault<T>(string str, TryParseDelegate<T> parse)
{
T value;
return parse(str, out value) ? value : default(T);
}
你可以这样称呼:
long l = ParseOrDefault<long>("12345", long.TryParse);
答案 2 :(得分:2)
为什么不使用简单的扩展方法?
Jon Skeet关于仅使用各种TryParse方法的默认结果的答案是好的。不过, 仍然是扩展方法的一个很好的方面。如果你这么做很多,你可以在一行代码而不是三行代码中完成相同的调用代码(加上可选择指定显式默认值)。
- 编辑 - 我做意识到在我原来的答案中,我基本上只是提供了一种略微不同的方式来做同样的事情,作者已经在做了。我今天早些时候抓到这个,当我真的很忙的时候,认为委托和自定义解析器的东西看起来可能有点多,然后在没有真正花时间完全理解问题的情况下找出答案是。遗憾。
如何使用(重载)扩展方法和反射?请参阅https://stackoverflow.com/a/4740544/618649
警告Emptor:我的例子没有说明你试图转换没有TryParse方法的类型。 GetMethod
调用应该有一些异常处理,依此类推。
/* The examples generates this output when run:
0
432123
-1
1/1/0001 12:00:00 AM
1/1/1970 12:00:00 AM
1/30/2013 12:00:00 PM
-1
12342.3233443
*/
class Program
{
static void Main ( string[] args )
{
Debug.WriteLine( "blah".Parse<Int64>() );
Debug.WriteLine( "432123".Parse<long>() );
Debug.WriteLine( "123904810293841209384".Parse<long>( -1 ) );
Debug.WriteLine( "this is not a DateTime value".Parse<DateTime>() );
Debug.WriteLine( "this is not a DateTime value".Parse<DateTime>( "jan 1, 1970 0:00:00".Convert<DateTime>() ) );
Debug.WriteLine( "2013/01/30 12:00:00".Parse<DateTime>() );
Debug.WriteLine( "this is not a decimal value".Parse<decimal>( -1 ) );
Debug.WriteLine( "12342.3233443".Parse<decimal>() );
}
}
static public class Extensions
{
static private Dictionary<Type,MethodInfo> s_methods = new Dictionary<Type, MethodInfo>();
static public T Parse<T> ( this string value ) where T : struct
{
return value.Parse<T>( default( T ) );
}
static public T Parse<T> ( this string value, T defaultValue ) where T : struct
{
// *EDITED* to cache the Reflection lookup--NOT thread safe
MethodInfo m = null;
if ( s_methods.ContainsKey( typeof( T ) ) )
{
m = s_methods[ typeof( T ) ];
}
else
{
m = typeof( T ).GetMethod(
"TryParse"
, BindingFlags.Public | BindingFlags.Static
, Type.DefaultBinder
, new[] { typeof( string ), typeof( T ).MakeByRefType() }
, null
);
s_methods.Add( typeof( T ), m );
}
var args = new object[] { value, null };
if( (bool)m.Invoke( null, args ))
{
return (T) args[ 1 ];
}
return defaultValue;
}
}
答案 3 :(得分:0)
这是我有时使用的方法。如果性能是一个主要问题,那么这不是一种可行的方法,因为try-catch块和对ChangeType的调用会比特定类型的TryParse慢一点。
public static bool TryFromString<T>(string value, out T convertedValue)
{
Type t = typeof(T);
convertedValue = default(T);
if (t.Name == "Nullable`1")
t = System.Nullable.GetUnderlyingType(t);
if (value != null)
{
try
{
convertedValue = (T)System.Convert.ChangeType(value, t, CultureInfo.CurrentCulture);
return true;
}
catch
{
}
}
return false;
}