You need to enable JavaScript to run this app.
优惠活动
大模型
产品
解决方案
定价
更多

C#中如何为带校验特性的方法参数自动注入校验逻辑

参数自动校验功能实现相关问题

基础背景与需求

我目前正在开展个人技术试验,开发了一批遵循IParameterValidator<in T>接口的参数校验器,接口定义如下:

public interface IParameterValidator<in T>
{
    public void Validate(T param);
}

以邮箱校验器为例,其实现代码如下:

public class EmailValidator: IParameterValidator<Email>
{
    private static readonly Regex EmailRegex = new(@"^[\w!#$%&'*+\-/=?\^_`{|}~]+(\.[\w!#$%&'*+\-/=?\^_`{|}~]+)*((([\-\w]+\.)+[a-zA-Z]{2,4})|(([0-9]{1,3}\.){3}[0-9]{1,3}))$");

    public void Validate(Email email)
    {
        if (!EmailRegex.IsMatch(email.ToString())) throw new InvalidParameterException(nameof(email));
    }
}

我的需求是:在任意代码位置为方法参数添加特性标注后,系统可自动匹配对应的校验器完成参数校验。为此我设计了如下ValidateParameterAttribute特性:

[AttributeUsage(AttributeTargets.Parameter, AllowMultiple = true)]
public class ValidateParameterAttribute: Attribute
{
    public Type ValidatorType { get; }

    public ValidateParameterAttribute(Type validatorType)
    {
        if (!validatorType.IsAssignableTo(typeof(IParameterValidator<>))) throw new("Must provide a validator!");
    }
}

据我所知,C#原生没有提供「识别方法/属性上的该特性即自动执行指定逻辑」的简易实现方式。我也知晓可以通过额外封装类扫描特性,实现类似MethodInfo.ValidatedInvoke(object[] params parms)的扩展方法完成调用,但我不希望采用这种侵入式的调用方案,希望该功能可以「开箱即用」。
我的预期效果是,如下标注了校验特性的方法:

public User CreateUser( [ValidateParameter(typeof(NameValidator))] Name name,
                        [ValidateParameter(typeof(EmailValidator))] Email email,
                        [ValidateParameter(typeof(AdultValidator))] DateOfBirth dateOfBirth)
{
    User user = new(name, email, dateOfBirth);
    _userRepository.Add(user);
    return user;
}

可以自动在方法执行起始位置注入对应的校验调用逻辑(校验器可存储在注册容器中,该部分实现暂不考虑),最终效果等价于如下代码:

public User CreateUser( [ValidateParameter(typeof(NameValidator))] Name name,
                        [ValidateParameter(typeof(EmailValidator))] Email email,
                        [ValidateParameter(typeof(AdultValidator))] DateOfBirth dateOfBirth)
{
    Activator.CreateInstance<NameValidator>().Validate(name);
    Activator.CreateInstance<EmailValidator>().Validate(email);
    Activator.CreateInstance<AdultValidator>().Validate(dateOfBirth);
    User user = new(name, email, dateOfBirth);
    _userRepository.Add(user);
    return user;
}

我查阅资料后了解到Source Generators(源生成器)可实现该需求,但我此前从未接触过源生成器开发,目前我已经编写了可生成校验代码片段的方法GetValidationString(MethodInfo method),代码如下:

private string GetValidationString(MethodInfo method)
{
    StringBuilder builder = new();
    ParameterInfo[] parameters = method.GetParameters();
    if (parameters.Length == 0) return string.Empty;
    foreach (ParameterInfo info in parameters)
    {
        ValidateParameterAttribute? validationAttribute = info.GetCustomAttribute<ValidateParameterAttribute>();
        if (validationAttribute is null) continue;
        builder.Append($"Activator.CreateInstance<{validationAttribute.ValidatorType.Name}>().Validate({info.Name});\n");
    }
    return builder.ToString();
}

我的第一个疑问是:如何通过源生成器将上述方法生成的校验代码,正确注入到目标方法的对应位置?

更新:Someta.Fody 方案异常问题

根据评论区的建议,我尝试使用Someta.Fody提供的方法拦截器实现该功能,目前编写了如下拦截特性:

[AttributeUsage(AttributeTargets.Method)]
public class ValidatedMethodAttribute: Attribute, IMethodInterceptor
{
    public object Invoke(MethodInfo methodInfo, object instance, Type[] typeArguments, object[] arguments, Func<object[], object> invoker)
    {
        Attribute[]? attributes = methodInfo.GetCustomAttributes().Where(att => att.GetType().IsAssignableTo(typeof(IParameterValidator<>))).ToArray();
        // 执行校验逻辑
        return invoker.Invoke(arguments);
    }
}

但构建项目时Fody抛出了未处理异常,我暂时无法定位问题原因,异常信息如下:

Fody: An unhandled exception occurred:
Exception:
Failed to execute weaver /home/james/.nuget/packages/someta.fody/1.2.1/build/../weaver/Someta.Fody.dll
Type:
System.Exception
StackTrace:
   at InnerWeaver.ExecuteWeavers() in C:\projects\fody\FodyIsolated\InnerWeaver.cs:line 222
   at InnerWeaver.Execute() in C:\projects\fody\FodyIsolated\InnerWeaver.cs:line 112
Source:
FodyIsolated
TargetSite:
Void ExecuteWeavers()
System.Type was somehow not found.  Aborting.
Type:
System.InvalidOperationException
StackTrace:
   at Someta.Fody.CecilExtensions.Initialize(ModuleDefinition moduleDefinition, TypeSystem typeSystem, AssemblyNameReference soMeta)
   at Someta.Fody.ModuleWeaver.Execute()
   at InnerWeaver.ExecuteWeavers() in C:\projects\fody\FodyIsolated\InnerWeaver.cs:line 186
Source:
Someta.Fody
TargetSite:
Boolean Initialize(Mono.Cecil.ModuleDefinition, Fody.TypeSystem, Mono.Cecil.AssemblyNameReference)

我注意到错误堆栈中存在Linux环境下不存在的C盘路径引用,想确认Linux构建环境是否是导致该异常的原因。

内容的提问来源于stack exchange,提问作者ScottishTapWater

相关产品推荐
方舟 Agent Plan

超全模态模型 × Harness 升级,最新支持 Deepseek-V4.1-Flash、GLM-5.3 系列、Doubao-Seedream-5.0-pro、Kimi-K3 (部分), 限时 9.9 元起

最近更新时间:2026.08.28 15:45:49