You need to enable JavaScript to run this app.
最新活动
大模型
产品
解决方案
定价
生态与合作
支持与服务
开发者
了解我们

如何优化通用表格打印方法?多集合差异化表头打印需求

你的直觉完全正确——这种靠一堆GetType()判断分支的实现确实不够优雅,不仅维护起来头疼,还违背了开闭原则(新增类型就得改老代码)。咱们来梳理几个更合理的优化方案,你可以根据自己的实际场景选最合适的:

1. 最直接:用方法重载替代泛型+类型判断

既然你要处理的类型是明确的(stringSortedList<someClass, string>Dictionary<int, List<someClass>>List<someClass>),直接为每个类型写重载方法是最清晰的选择。这种方式编译时就能确保类型安全,不用再做 runtime 类型判断和强制转换。

示例代码:

// 处理string的重载
private void PrintExample(string fileName, string message)
{
    using (StreamWriter sw = new StreamWriter(fileName, true))
    {
        // 实现string的打印逻辑
        sw.WriteLine($"字符串内容:{message}");
    }
}

// 处理SortedList<someClass, string>的重载
private void PrintExample(string fileName, SortedList<someClass, string> list)
{
    using (StreamWriter sw = new StreamWriter(fileName, true))
    {
        PrintTableHeader(sw, "| 1 | 2 | 3 |");
        foreach (var kvp in list)
        {
            // 替换为实际的字段打印逻辑
            sw.WriteLine($"| {kvp.Key.Id} | {kvp.Value} | 占位 |");
        }
        PrintTableFooter(sw);
    }
}

// 处理Dictionary<int, List<someClass>>的重载
private void PrintExample(string fileName, Dictionary<int, List<someClass>> dict)
{
    using (StreamWriter sw = new StreamWriter(fileName, true))
    {
        PrintTableHeader(sw, "| SOMETHING | Name | Something2 |");
        foreach (var kvp in dict)
        {
            foreach (var item in kvp.Value)
            {
                sw.WriteLine($"| {kvp.Key} | {item.Name} | {item.Something2} |");
            }
            PrintDivider(sw);
        }
        sw.WriteLine();
    }
}

// 处理List<someClass>的重载
private void PrintExample(string fileName, List<someClass> list)
{
    using (StreamWriter sw = new StreamWriter(fileName, true))
    {
        PrintTableHeader(sw, "| other header | other header 7777 |");
        foreach (var item in list)
        {
            sw.WriteLine($"| {item.OtherProp} | {item.OtherProp7777} |");
        }
        PrintTableFooter(sw);
    }
}

// 提取重复逻辑为辅助方法,减少冗余
private void PrintTableHeader(StreamWriter sw, string headerLine)
{
    var divider = new string('-', headerLine.Length);
    sw.WriteLine(divider);
    sw.WriteLine(headerLine);
    sw.WriteLine(divider);
}

private void PrintTableFooter(StreamWriter sw)
{
    sw.WriteLine(new string('-', 50));
    sw.WriteLine();
}

private void PrintDivider(StreamWriter sw)
{
    sw.WriteLine(new string('-', 50));
}

这种方案的优点:

  • 代码结构清晰,每个方法只负责一种类型的打印逻辑
  • 编译时类型安全,避免了as强制转换的风险
  • 扩展简单,新增类型只需要加新的重载方法,不用修改原有代码

2. 更灵活:策略模式(适合动态扩展场景)

如果以后需要频繁新增打印类型,或者希望打印逻辑和业务代码解耦,可以用策略模式。核心是把每种类型的打印逻辑封装成独立的策略类,通过字典或依赖注入来匹配对应的策略。

首先定义打印策略接口:

public interface IPrintStrategy<T>
{
    void Print(string fileName, T data);
}

然后为每个类型实现策略:

public class StringPrintStrategy : IPrintStrategy<string>
{
    public void Print(string fileName, string data)
    {
        using (StreamWriter sw = new StreamWriter(fileName, true))
        {
            sw.WriteLine($"字符串内容:{data}");
        }
    }
}

public class SortedListPrintStrategy : IPrintStrategy<SortedList<someClass, string>>
{
    public void Print(string fileName, SortedList<someClass, string> data)
    {
        using (StreamWriter sw = new StreamWriter(fileName, true))
        {
            var header = "| 1 | 2 | 3 |";
            var divider = new string('-', header.Length);
            sw.WriteLine(divider);
            sw.WriteLine(header);
            sw.WriteLine(divider);
            foreach (var kvp in data)
            {
                sw.WriteLine($"| {kvp.Key.Id} | {kvp.Value} | 占位 |");
            }
            sw.WriteLine(divider);
            sw.WriteLine();
        }
    }
}

// 其他类型的策略类同理...

最后在主类中注册并调用策略:

private readonly Dictionary<Type, object> _printStrategies = new();

// 构造函数中注册策略
public YourClassName()
{
    _printStrategies.Add(typeof(string), new StringPrintStrategy());
    _printStrategies.Add(typeof(SortedList<someClass, string>), new SortedListPrintStrategy());
    // 注册其他策略...
}

// 统一入口方法
private void PrintExample<T>(string fileName, T data)
{
    if (_printStrategies.TryGetValue(typeof(T), out var strategy))
    {
        ((IPrintStrategy<T>)strategy).Print(fileName, data);
    }
    else
    {
        throw new NotSupportedException($"暂不支持类型{typeof(T)}的打印");
    }
}

这种方案的优点:

  • 打印逻辑和业务代码完全解耦,符合单一职责原则
  • 可以动态添加/替换策略,甚至在运行时切换打印格式
  • 适合复杂场景,比如多个模块需要复用打印逻辑

3. 利用多态:让数据类型自己负责打印(若可修改实体类)

如果someClass是你自己定义的,可以让实体类实现统一的打印接口,把打印逻辑封装到实体内部,再配合扩展方法处理集合的表头和循环。

首先定义打印接口:

public interface IPrintable
{
    void PrintToStream(StreamWriter sw);
}

someClass实现接口:

public class someClass : IPrintable
{
    public string Name { get; set; }
    public int Something2 { get; set; }
    public string OtherProp { get; set; }

    public void PrintToStream(StreamWriter sw)
    {
        // 这里可以根据不同的打印场景重载,或者保持基础格式
        sw.WriteLine($"| {Name} | {Something2} |");
    }
}

然后写扩展方法处理集合:

public static class PrintExtensions
{
    public static void PrintAsTable<T>(this IEnumerable<T> items, StreamWriter sw, string header) where T : IPrintable
    {
        var divider = new string('-', header.Length);
        sw.WriteLine(divider);
        sw.WriteLine(header);
        sw.WriteLine(divider);
        foreach (var item in items)
        {
            item.PrintToStream(sw);
        }
        sw.WriteLine(divider);
        sw.WriteLine();
    }

    public static void PrintAsTable<TKey>(this Dictionary<TKey, List<someClass>> dict, StreamWriter sw, string header)
    {
        var divider = new string('-', header.Length);
        sw.WriteLine(divider);
        sw.WriteLine(header);
        sw.WriteLine(divider);
        foreach (var kvp in dict)
        {
            sw.WriteLine($"| {kvp.Key} |");
            foreach (var item in kvp.Value)
            {
                item.PrintToStream(sw);
            }
            sw.WriteLine(divider);
        }
        sw.WriteLine();
    }
}

调用时就非常简洁:

private void PrintExample(string fileName, List<someClass> list)
{
    using (StreamWriter sw = new StreamWriter(fileName, true))
    {
        list.PrintAsTable(sw, "| other header | other header 7777 |");
    }
}

这种方案的优点:

  • 实体类自己控制打印格式,符合封装原则
  • 扩展方法可以复用,减少重复代码
  • 后续修改实体的打印格式,只需要修改实体类的实现

总结

如果你的场景比较简单,优先选方法重载,它最直接、易维护;如果以后需要频繁扩展打印类型,或者想解耦逻辑,就用策略模式;如果能修改实体类的代码,多态+扩展方法会让代码更简洁优雅。

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

火山引擎 最新活动