我有以下根據輸入型別回傳“值決議器”委托的方法。它作業正常,但我想擺脫 switch 陳述句和型別檢查,并能夠為任何具有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.
}
幾天來,我一直在用頭撞墻,但沒有取得多大成功。非常感謝您提供的任何幫助。謝謝!
uj5u.com熱心網友回復:
這似乎可以解決問題:
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;
}
在 dotnetfiddle.net 上查看。
希望行內評論能解釋發生了什么。如果沒有,請告訴我,我會改進它們。
請注意,如果輸入是null,這將遵循您的代碼呼叫約定TryParse(null, out var res)。這似乎不太明智。
轉載請註明出處,本文鏈接:https://www.uj5u.com/gongcheng/430768.html
