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

如何为record类统一配置validateString的Serilog泛型日志上下文?

问题:优化C# Record中validateString方法的日志上下文指定方式

我有多个类似下面的Record类型,都继承自MavApiRecordBase:

internal record Carrier : MavApiRecordBase{
    /// <summary>
    /// Identificativo Carrier
    /// </summary>
    [JsonPropertyName("id")]
    public Int64 Id { get; set; }
    /// <summary>
    /// Tag (nome) del Carrier
    /// </summary>
    [JsonPropertyName("tag")]
    public string Name { get => Name; set => Name = validateString<Carrier>(value, 64); }
    /// <summary>
    /// Tag (nome) del Payload a cui il carrier risulta associato. null se non associato ad alcun payload.
    /// </summary>
    [JsonPropertyName("payload")]
    public string? PayloadTag { get => Name; set => Name = validateString<Carrier>(value, 64); }
}

其中validateString是多个Record共享的静态方法,它的泛型参数仅用于Serilog的Log.ForContext<TLogContext>(),用来标记日志的上下文类型。

现在我每次要修改某个Record里所有validateString<>的泛型参数,只能用Visual Studio的查找替换,操作很麻烦。我想把这个泛型类型绑定到某个可统一修改的静态变量/属性,比如在抽象基类MavApiRecordBase里定义一个可重写的protected成员:

protected override Type defLogCtx {get; set;} = typeof(Carrier);  
// 期望能这样调用
validateString<defLogCtx>(..) // 在Carrier类内部,这样改一次就能统一所有调用
// 或者干脆去掉泛型参数

当前validateString的实现如下:

public static string validateString<TLogContext>(string? str, int maxLen) {
    if (str is null)
        Log.ForContext<TLogContext>().Error("rogue null reference. used empty string instead.");
    else if (str.Length > maxLen)
        Log.ForContext<TLogContext>().Warning("max string length is {l}. Got {val}.", maxLen, str);

    return str.Truncate(maxLen) ?? "";
}

我刚接触C#,想知道这种思路是否可行,有没有更合理的实现方式。


解决方案说明

首先明确:直接用变量作为泛型参数不可行

C#的泛型参数必须是编译期确定的常量类型,不能用运行时的Type变量(比如你定义的defLogCtx)来作为泛型参数,所以validateString<defLogCtx>(..)这种写法是编译不通过的。需要调整实现方式:

方案1:将validateString改为非泛型,接收Type参数

修改validateString,把泛型参数换成Type类型的参数,同时使用Serilog的非泛型ForContext方法:

public static string ValidateString(string? str, int maxLen, Type logContextType)
{
    if (str is null)
        Log.ForContext(logContextType).Error("rogue null reference. used empty string instead.");
    else if (str.Length > maxLen)
        Log.ForContext(logContextType).Warning("max string length is {l}. Got {val}.", maxLen, str);

    return str.Truncate(maxLen) ?? "";
}

然后在基类里定义一个可重写的属性,默认返回当前子类的类型:

public abstract class MavApiRecordBase
{
    protected virtual Type LogContextType => GetType();
}

最后在子类的属性里调用时,直接传入这个属性值即可:

internal record Carrier : MavApiRecordBase
{
    [JsonPropertyName("id")]
    public long Id { get; set; }

    // 注意:你原来的get/set写法会导致无限递归,必须修正!
    private string _name = "";
    [JsonPropertyName("tag")]
    public string Name 
    { 
        get => _name; 
        set => _name = ValidateString(value, 64, LogContextType); 
    }

    private string? _payloadTag;
    [JsonPropertyName("payload")]
    public string? PayloadTag 
    { 
        get => _payloadTag; 
        set => _payloadTag = ValidateString(value, 64, LogContextType); 
    }
}

方案2:使用泛型基类封装调用

如果不想修改validateString的泛型实现,可以用泛型基类来统一指定日志上下文类型:

public abstract class MavApiRecordBase<T> where T : MavApiRecordBase<T>
{
    protected string ValidateString(string? str, int maxLen)
    {
        return YourStaticClass.validateString<T>(str, maxLen);
    }
}

子类继承时只需指定自身作为泛型参数,之后所有属性里直接调用封装好的实例方法即可:

internal record Carrier : MavApiRecordBase<Carrier>
{
    [JsonPropertyName("id")]
    public long Id { get; set; }

    private string _name = "";
    [JsonPropertyName("tag")]
    public string Name 
    { 
        get => _name; 
        set => _name = ValidateString(value, 64); 
    }

    private string? _payloadTag;
    [JsonPropertyName("payload")]
    public string? PayloadTag 
    { 
        get => _payloadTag; 
        set => _payloadTag = ValidateString(value, 64); 
    }
}

额外注意:修正原代码中的无限递归问题

你原来的属性写法get => Name; set => Name = ...会导致无限递归:get访问Name属性本身,set又赋值给Name属性,运行时会直接栈溢出。必须改成用私有字段存储值,或者使用自动属性结合初始化逻辑。


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

相关产品推荐
方舟 Agent Plan

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

最近更新时间:2026.07.11 08:31:13