我正在尝试验证我的 Unity
MonoBehavior
,以便在脚本设置不正确时更加明显。我从我所做的其他 C# 工作中了解到 FluentValidation,因此我为 Unity 设置了 Nuget 并安装了它。
问题在于,当 MonoBehavior 上的 GameObject 未初始化时,由于 Unity 的行为覆盖,FluentValidation 的表达式会抛出错误而不是返回 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);
}
}
这是我的验证器类:
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&)
编辑:
我不再在 Unity 中使用 FluentValidations。我有自己的类,只是在需要时抛出条件,但请注意,Unity 有一个内置的 RequireComponent 属性,可以处理大量情况。
原文:
我没有弄清楚如何使用
Expression
s 以通用方式完成此操作,但我确实弄清楚了如何专门针对 GameObject
s 解决此问题,这是我试图解决的问题的原因。
总体思路是,只要表达式中只有一条语句,就可以在
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)
。