使用 "TryParse()" 方法构建可以处理任何 type/class 的表达式树
Build an expression tree that can handle any type/class with a "TryParse()" method
我有以下方法,return根据输入类型是一个“值解析器”委托。它工作正常,但我想摆脱 switch 语句和类型检查,并能够 return 具有 TryParse()
方法的任何类型的值解析器委托。
internal static Func<object?, (bool isSuccess, object value)>? ValueParser(this Type type)
{
type = Nullable.GetUnderlyingType(type) ?? type;
if (type.IsEnum)
return input => (Enum.TryParse(type, ToString(input), out var res), res!);
switch (Type.GetTypeCode(type))
{
case TypeCode.String:
return input => (true, input!);
case TypeCode.Boolean:
return input => (bool.TryParse(ToString(input), out var res), res);
case TypeCode.DateTime:
return input => (DateTime.TryParse(ToString(input), out var res), res);
//other supported types go here...
case TypeCode.Object:
if (type == Types.Guid)
{
return input => (Guid.TryParse(ToString(input), out var res), res);
}
else if (type == Types.TimeSpan)
{
return input => (TimeSpan.TryParse(ToString(input), out var res), res!);
}
break;
}
return null; //unsupported types will cause a null return
static string? ToString(object? value)
{
if (value is string x)
return x;
return value?.ToString();
}
}
我相信解决方案是构建一个如下所示的表达式树。但我对如何正确构建表达式树一无所知。到目前为止,我只有以下内容:
internal static Func<object?, (bool isSuccess, object value)>? ValueParser(this Type type)
{
type = Nullable.GetUnderlyingType(type) ?? type;
var inputParam = Expression.Parameter(typeof(string), "input");
if (type == Types.String)
{
//no need for conversion if input type is string. so delegate should simply return a tuple (true,"inputValue").
var returnTarget = Expression.Label(type);
var returnCall = Expression.Return(returnTarget, inputParam);
}
var parsedVar = Expression.Variable(type, "parsed");
var tryParseCall = Expression.Call(
type,
"TryParse",
null,
inputParam,
parsedVar);
//need to compile the expression and return here.
//if the input type doesn't have a TryParse() method, null should be returned.
//also not sure if we need Expression.Convert() to handle value types.
}
几天来,我一直在用头撞墙,但没有成功。非常感谢您提供的任何帮助。谢谢!
这似乎可以解决问题:
private static readonly MethodInfo toStringMethod = typeof(object).GetMethod("ToString")!;
private static readonly ConstructorInfo valueTupleConstructor = typeof(ValueTuple<bool, object>).GetConstructor(new[] { typeof(bool), typeof(object) })!;
internal static Func<object?, (bool isSuccess, object value)>? ValueParser(Type type)
{
type = Nullable.GetUnderlyingType(type) ?? type;
if (type == typeof(string))
return input => (true, input!);
if (type.IsEnum)
return input => (Enum.TryParse(type, input?.ToString(), out var res), res!);
// Try and find a suitable TryParse method on Type
var tryParseMethod = type.GetMethod("TryParse", BindingFlags.Static | BindingFlags.Public, new[] { typeof(string), type.MakeByRefType() });
// None found or returns the wrong type? Return null.
if (tryParseMethod == null || tryParseMethod.ReturnType != typeof(bool))
return null;
// The 'object' parameter passed into our delegate
var inputParameter = Expression.Parameter(typeof(object), "input");
// 'input == null ? (string)null : input.ToString()'
var toStringConversion = Expression.Condition(
Expression.ReferenceEqual(inputParameter, Expression.Constant(null, typeof(object))),
Expression.Constant(null, typeof(string)),
Expression.Call(inputParameter, toStringMethod));
// 'res' variable used as the out parameter to the TryParse call
var resultVar = Expression.Variable(type, "res");
// 'isSuccess' variable to hold the result of calling TryParse
var isSuccessVar = Expression.Variable(typeof(bool), "isSuccess");
// To finish off, we need to following sequence of statements:
// - isSuccess = TryParse(input.ToString(), res)
// - new ValueTuple<bool, object>(isSuccess, (object)res)
// A sequence of statements is done using a block, and the result of the final
// statement is the result of the block
var tryParseCall = Expression.Call(tryParseMethod, toStringConversion, resultVar);
var block = Expression.Block(new[] { resultVar, isSuccessVar },
Expression.Assign(isSuccessVar, tryParseCall),
Expression.New(valueTupleConstructor, isSuccessVar, Expression.Convert(resultVar, typeof(object))));
// Put it all together
var lambda = Expression.Lambda<Func<object?, (bool, object)>>(block, inputParameter).Compile();
return lambda;
}
希望内联评论能解释发生了什么。如果没有,请告诉我,我会改进它们。
请注意,如果输入是 null
,这将遵循您的代码调用 TryParse(null, out var res)
的约定。这似乎不太明智。
我有以下方法,return根据输入类型是一个“值解析器”委托。它工作正常,但我想摆脱 switch 语句和类型检查,并能够 return 具有 TryParse()
方法的任何类型的值解析器委托。
internal static Func<object?, (bool isSuccess, object value)>? ValueParser(this Type type)
{
type = Nullable.GetUnderlyingType(type) ?? type;
if (type.IsEnum)
return input => (Enum.TryParse(type, ToString(input), out var res), res!);
switch (Type.GetTypeCode(type))
{
case TypeCode.String:
return input => (true, input!);
case TypeCode.Boolean:
return input => (bool.TryParse(ToString(input), out var res), res);
case TypeCode.DateTime:
return input => (DateTime.TryParse(ToString(input), out var res), res);
//other supported types go here...
case TypeCode.Object:
if (type == Types.Guid)
{
return input => (Guid.TryParse(ToString(input), out var res), res);
}
else if (type == Types.TimeSpan)
{
return input => (TimeSpan.TryParse(ToString(input), out var res), res!);
}
break;
}
return null; //unsupported types will cause a null return
static string? ToString(object? value)
{
if (value is string x)
return x;
return value?.ToString();
}
}
我相信解决方案是构建一个如下所示的表达式树。但我对如何正确构建表达式树一无所知。到目前为止,我只有以下内容:
internal static Func<object?, (bool isSuccess, object value)>? ValueParser(this Type type)
{
type = Nullable.GetUnderlyingType(type) ?? type;
var inputParam = Expression.Parameter(typeof(string), "input");
if (type == Types.String)
{
//no need for conversion if input type is string. so delegate should simply return a tuple (true,"inputValue").
var returnTarget = Expression.Label(type);
var returnCall = Expression.Return(returnTarget, inputParam);
}
var parsedVar = Expression.Variable(type, "parsed");
var tryParseCall = Expression.Call(
type,
"TryParse",
null,
inputParam,
parsedVar);
//need to compile the expression and return here.
//if the input type doesn't have a TryParse() method, null should be returned.
//also not sure if we need Expression.Convert() to handle value types.
}
几天来,我一直在用头撞墙,但没有成功。非常感谢您提供的任何帮助。谢谢!
这似乎可以解决问题:
private static readonly MethodInfo toStringMethod = typeof(object).GetMethod("ToString")!;
private static readonly ConstructorInfo valueTupleConstructor = typeof(ValueTuple<bool, object>).GetConstructor(new[] { typeof(bool), typeof(object) })!;
internal static Func<object?, (bool isSuccess, object value)>? ValueParser(Type type)
{
type = Nullable.GetUnderlyingType(type) ?? type;
if (type == typeof(string))
return input => (true, input!);
if (type.IsEnum)
return input => (Enum.TryParse(type, input?.ToString(), out var res), res!);
// Try and find a suitable TryParse method on Type
var tryParseMethod = type.GetMethod("TryParse", BindingFlags.Static | BindingFlags.Public, new[] { typeof(string), type.MakeByRefType() });
// None found or returns the wrong type? Return null.
if (tryParseMethod == null || tryParseMethod.ReturnType != typeof(bool))
return null;
// The 'object' parameter passed into our delegate
var inputParameter = Expression.Parameter(typeof(object), "input");
// 'input == null ? (string)null : input.ToString()'
var toStringConversion = Expression.Condition(
Expression.ReferenceEqual(inputParameter, Expression.Constant(null, typeof(object))),
Expression.Constant(null, typeof(string)),
Expression.Call(inputParameter, toStringMethod));
// 'res' variable used as the out parameter to the TryParse call
var resultVar = Expression.Variable(type, "res");
// 'isSuccess' variable to hold the result of calling TryParse
var isSuccessVar = Expression.Variable(typeof(bool), "isSuccess");
// To finish off, we need to following sequence of statements:
// - isSuccess = TryParse(input.ToString(), res)
// - new ValueTuple<bool, object>(isSuccess, (object)res)
// A sequence of statements is done using a block, and the result of the final
// statement is the result of the block
var tryParseCall = Expression.Call(tryParseMethod, toStringConversion, resultVar);
var block = Expression.Block(new[] { resultVar, isSuccessVar },
Expression.Assign(isSuccessVar, tryParseCall),
Expression.New(valueTupleConstructor, isSuccessVar, Expression.Convert(resultVar, typeof(object))));
// Put it all together
var lambda = Expression.Lambda<Func<object?, (bool, object)>>(block, inputParameter).Compile();
return lambda;
}
希望内联评论能解释发生了什么。如果没有,请告诉我,我会改进它们。
请注意,如果输入是 null
,这将遵循您的代码调用 TryParse(null, out var res)
的约定。这似乎不太明智。