如何将 Expression<Func<T、TProperty>> 与返回 null 的 TryCatch 包装在一起?



我试图验证我的UnityMonoBehavior,以便在脚本设置不正确时更加明显。我从其他c#工作中了解到FluentValidation,所以我为Unity设置了Nuget并安装了它。

问题是,当MonoBehavior上的GameObject没有初始化时,FluentValidation的表达式会抛出一个错误,而不是由于Unity的行为覆盖而返回null。

我试图通过创建一个扩展方法来解决这个问题,该方法将捕获表达式中抛出的任何异常并返回null。我不熟悉使用Expression类构建表达式,但我不确定该怎么做。运行此命令会导致编辑器控制台中出现以下错误:

InvalidOperationException: No coercion operator is defined between types 'System.Func`2[DialogController,UnityEngine.GameObject]' and 'UnityEngine.GameObject'.
System.Linq.Expressions.Expression.GetUserDefinedCoercionOrThrow (System.Linq.Expressions.ExpressionType coercionType, System.Linq.Expressions.Expression expression, System.Type convertToType) (at <351e49e2a5bf4fd6beabb458ce2255f3>:0)
System.Linq.Expressions.Expression.Convert (System.Linq.Expressions.Expression expression, System.Type type, System.Reflection.MethodInfo method) (at <351e49e2a5bf4fd6beabb458ce2255f3>:0)
System.Linq.Expressions.Expression.Convert (System.Linq.Expressions.Expression expression, System.Type type) (at <351e49e2a5bf4fd6beabb458ce2255f3>:0)
ValidationExtensions.SafeRuleFor[T,TProperty] (FluentValidation.AbstractValidator`1[T] validator, System.Linq.Expressions.Expression`1[TDelegate] expression) (at Assets/Scripts/ValidationExtensions.cs:12)
DialogControllerValidation.WhenAsync (System.Func`3[T1,T2,TResult] predicate, System.Action action) (at Assets/Scripts/DialogControllerValidation.cs:10)
DialogController.OnValidate () (at Assets/Scripts/DialogController.cs:170)

这是我的MonoBehavior:

public class DialogController: MonoBehavior {
public GameObject content;

private void OnValidate()
{
new DialogControllerValidation().ValidateAndThrow(this);
}
}
这是我的Validator类:
public class DialogControllerValidation : AbstractValidator<DialogController>
{
public DialogControllerValidation()
{
this.SafeRuleFor(x => x.content).NotNull();
}
}

这是我的扩展方法:

public static class ValidationExtensions
{
public static IRuleBuilderInitial<T, TProperty> SafeRuleFor<T, TProperty>(this AbstractValidator<T> validator,
Expression<Func<T, TProperty>> expression)
{
var tryExpression = Expression.TryCatch(
Expression.Block(typeof(TProperty), Expression.Convert(expression, typeof(TProperty))),
Expression.Catch(typeof(TProperty), Expression.Constant(null))
);
return validator.RuleFor(Expression.Lambda<Func<T, TProperty>>(
tryExpression,
Expression.Parameter(typeof(T), "t")
)
);
}
}

编辑:如果我将Expression.Convert(expression, typeof(TProperty))替换为expression,即:

public static class ValidationExtensions
{
public static IRuleBuilderInitial<T, TProperty> SafeRuleFor<T, TProperty>(this AbstractValidator<T> validator,
Expression<Func<T, TProperty>> expression)
{
var tryExpression = Expression.TryCatch(
Expression.Block(typeof(TProperty), expression),
Expression.Catch(typeof(TProperty), Expression.Constant(null))
);
return validator.RuleFor(Expression.Lambda<Func<T, TProperty>>(
tryExpression,
Expression.Parameter(typeof(T), "t")
)
);
}
}

我得到的错误是

ArgumentException: Argument types do not match
System.Linq.Expressions.Expression.BlockCore (System.Type type, System.Collections.ObjectModel.ReadOnlyCollection`1[T] variables, System.Collections.ObjectModel.ReadOnlyCollection`1[T] expressions) (at <351e49e2a5bf4fd6beabb458ce2255f3>:0)
System.Linq.Expressions.Expression.Block (System.Type type, System.Collections.Generic.IEnumerable`1[T] variables, System.Collections.Generic.IEnumerable`1[T] expressions) (at <351e49e2a5bf4fd6beabb458ce2255f3>:0)
System.Linq.Expressions.Expression.Block (System.Type type, System.Collections.Generic.IEnumerable`1[T] expressions) (at <351e49e2a5bf4fd6beabb458ce2255f3>:0)
System.Linq.Expressions.Expression.Block (System.Type type, System.Linq.Expressions.Expression[] expressions) (at <351e49e2a5bf4fd6beabb458ce2255f3>:0)
ValidationExtensions.SafeRuleFor[T,TProperty] (FluentValidation.AbstractValidator`1[T] validator, System.Linq.Expressions.Expression`1[TDelegate] expression) (at Assets/Scripts/ValidationExtensions.cs:12)
DialogControllerValidation.WhenAsync (System.Func`3[T1,T2,TResult] predicate, System.Action action) (at Assets/Scripts/DialogControllerValidation.cs:10)
DialogController.OnValidate () (at Assets/Scripts/DialogController.cs:170)
UnityEngine.GUIUtility:ProcessEvent(Int32, IntPtr, Boolean&)

我没有弄清楚如何使用Expressions以通用的方式完成此操作,但我确实弄清楚如何专门为GameObjects解决此问题,这是我试图解决的问题的原因。

一般的想法是,只要表达式中只有一条语句,就可以在Expression中运行方法。

这是我想到的扩展方法:

public static class ValidationExtensions
{
public static IRuleBuilderInitial<T, GameObject> SafeRuleFor<T>(this AbstractValidator<T> validator,
Func<T, GameObject> func)
{
return validator.RuleFor(x => SafeWrap(func(x)));
}
private static GameObject SafeWrap(GameObject gameObject)
{
return gameObject ? gameObject : null;
}
}

如果您希望将输入参数保留为表达式,您可以将func(x)替换为expression.Compile().Invoke(x)

最新更新