如何用返回 null 的 TryCatch 包装 Expression<Func<T, TProperty>>?

问题描述 投票:0回答:1

我正在尝试验证我的 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&)
c# unity-game-engine fluentvalidation
1个回答
0
投票

编辑:

我不再在 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)

© www.soinside.com 2019 - 2024. All rights reserved.