在 C# 中,是否有更聪明的方法来重载一组具有多个签名的不同名称的方法?

In C# Is there a smarter way to overload a set of methods with different names with a number of signatures?

我正在构建一个日志记录 class,它根据方法名称对消息进行不同的格式化。我希望能够以与 Console.WriteLine() 的工作方式类似的方式使用它。重载工作得很好,但我想用一组不同的方法来做到这一点,名称是格式化选项的决定因素。

我正在寻找是否有 'smarter' 方法来执行此操作,然后一遍又一遍地编写同一方法的每个版本的每个重载方法。感觉就是这样,容易出错。

一些我想如何使用它的例子:

        Logger Log = new Logger();
        Log.Prod ("Main Log Window");
        Log.Warn ("This is a Warning Message");
        Log.Error ("This is an Error Message");
        Log.Network ("This is a Network Message");
        Log.Verbose ("This is for Verbose Message Reporting");
        Log.Processing ("Analaysis Related Message");

我的意思是如何使用 Console.WriteLine,我的意思是像这样:

        String Name = "Sam";
        String Day = "Monday";
        int hour = 11;
        int minute = 30;
        Console.WriteLine ("Hello World");
        Console.WriteLine ("Hello {0}", Name);
        Console.WriteLine ("{0}, the time is {1}:{2}", Name, hour, minute);

所以,我最终会得到的更像是这样:

        Logger Log = new Logger();
        Log.Prod ("Staring Application");
        Log.Warn ("Data {0} looks Corrupt",data);
        Log.Error ("Error: {0}", message);
        Log.Network ("User {0} logged in from {1}", user, network);
        Log.Verbose ("This is for Verbose Message Reporting");
        Log.Processing ("Analaysis Related Message");

现在,我知道我可以为同一例程的每个版本编写每个重载方法(请记住,每个版本的消息格式略有不同),并且我可以将某种 'code' 就可以了,比如:

        Log.Write (Log.Error ,"Error: {0}", message);

... 和 Log.Error 是某种枚举或其他什么,但这很讨厌。

我想做的是将这一切都分解成一个 case 语句来处理格式,然后将其输入到输出中。我需要编写的大部分代码都是这种重载中继,它们 99% 重复了同样的事情。一定有更聪明的方法。

我在其他语言中看到过一些可能会执行此操作的方法,但我正在寻找一种 C#(.Net Framework 4.8)方法来执行此操作(如果可能的话)。 'cleanest' 和 'smarter' 除了将它 全部 写出来之外,还有什么方法可以做到这一点?

我已经研究过重载、覆盖、模板、接口等。但到目前为止,它看起来好像最终会是相同的结果或更糟。

-- 编辑

我有一个旧版本做了这样的事情:

    ...

    public static void Log (Type LogType, object Msg, object [] args ) {
        // Using Type specifier and format options.
        String NewMsg = String.Format (Msg.ToString(), args);
        Logger.Log ((int) LogType, NewMsg);
    }

    public static void Log (Type LogType, object Msg, object arg1, object arg2 ) {
        // Using Type specifier and format options.
        String NewMsg = String.Format (Msg.ToString(), arg1, arg2);
        Logger.Log ((int) LogType, NewMsg);
    }

    public static void Log (Type LogType, object Msg, object arg1, object arg2, object arg3 ) {
        // Using Type specifier and format options.
        String NewMsg = String.Format (Msg.ToString(), arg1, arg2, arg3);
        Logger.Log ((int) LogType, NewMsg);
    }

    ...

我想看看我是否可以摆脱那个 'Type' 参数并使用函数的名称来转发它。也许那是某种模板或重载。我想我可能已经从 Console.WriteLine 做了一些 copy/paste,(已经有一段时间了)或者至少用它作为起点。

我知道还有很长的路要走,我只是希望有一个 'smarter' 版本,但到目前为止,似乎还没有。

这可能有错误,只是把它放在一起作为长途的例子...

    ...

        public static void Warn(object Msg, object[] args) {
            // Using Type specifier and format options.
            String NewMsg = String.Format (Msg.ToString(), args);
            Logger.Log(1, NewMsg);
        }

        public static void Warn(object Msg, object arg1, object arg2) {
            // Using Type specifier and format options.
            String NewMsg = String.Format (Msg.ToString(), arg1, arg2);
            Logger.Log(1, NewMsg);
        }

        public static void Warn(object Msg, object arg1, object arg2, object arg3) {
            // Using Type specifier and format options.
            String NewMsg = String.Format (Msg.ToString(), arg1, arg2, arg3);
            Logger.Log(1, NewMsg);
        }

    ...

        public static void Error(object Msg, object[] args) {
            // Using Type specifier and format options.
            String NewMsg = String.Format (Msg.ToString(), args);
            Logger.Log(2, NewMsg);
        }

        public static void Error(object Msg, object arg1, object arg2) {
            // Using Type specifier and format options.
            String NewMsg = String.Format (Msg.ToString(), arg1, arg2);
            Logger.Log(2, NewMsg);
        }

        public static void Error(object Msg, object arg1, object arg2, object arg3) {
            // Using Type specifier and format options.
            String NewMsg = String.Format (Msg.ToString(), arg1, arg2, arg3);
            Logger.Log(2, NewMsg);
        }

    ...

所以,如果我按照自己的意愿做,那可能会很长,我可以,我只是想知道是否有更好的方法。

这是我学习重载的时候,所以,是的。

--- 编辑:工作:

我不得不采取一些技巧来让它旋转起来,但万一有人需要它,这就是我想出的:

工作示例代码片段:

        Logger Log = new Logger();
        String name = "George";
        long records = 200000;
        String network = "someplace.com";
        int hour = 10;
        int minute = 30;
        Log.Prod ();
        Log.Warn ("Project Starting");
        Log.Warn (network);
        Log.Prod ("Hello {0}!", name);
        Log.Prod ($"Hello {name}!");
        Log.Processing("{0} Processed", records);
        Log.Processing($"{records} Processed");
        Log.Network($"{name} logged in from {network} at {hour}:{minute}");
        Log.Network("{0} logged in from {1} at {2}:{3}", name, network, hour, minute);

现在,我这里还没有格式的东西,(不相关,但值是可以使用的,它将进入控制台日志 window 所以它们将被着色和各种邮票将包装它们,但这是稍后的简单部分。)

public class Logger {

    public Logger () {
        if (LogSubsystem.Init == true) {
            return;
        } else {
            // Do some setup stuff here.
        }
    }

    public void Prod(params object[] args) {
        LogMessage(1, args);
    }

    public void StatusBar(params object[] args) {
        LogMessage(2, args);
    }

    public void TitleBar(params object[] args) {
        LogMessage(3, args);
    }

    public void Verbose(params object[] args) {
        LogMessage(4, args);
    }

    public void Status(params object[] args) {
        LogMessage(5, args);
    }

    public void Network(params object[] args) {
        LogMessage(6, args);
    }

    public void Account(params object[] args) {
        LogMessage(7, args);
    }

    public void Error(params object[] args) {
        LogMessage(8, args);
    }

    public void Warn(params object[] args) {
        LogMessage(9, args);
    }

    public void Processing(params object[] args) {
        LogMessage(10, args);
    }

    public void DebugLow(params object[] args) {
        LogMessage(11, args);
    }

    public void DebugMid(params object[] args) {
        LogMessage(12, args);
    }

    public void DebugHigh(params object[] args) {
        LogMessage (13, args);
    }

    private void LogMessage(int mode, object [] args) {

        var property = typeof(ICollection).GetProperty("Count");
        int count = (int)property.GetValue(args, null);

        ... many things missing....

        if (count == 0) {
            Console.WriteLine ("");
        }
        if (count == 1) {
            Console.WriteLine ("{0}", args[0]);
        }
        if (count > 1) {
            ArraySegment<object> ASargs = new ArraySegment<object>(args, 1, (count-1));
            object [] SubArgs = ArraySegmentToArray(ASargs);
            Console.WriteLine (args[0].ToString(), SubArgs);
        }
    }

    private object [] ArraySegmentToArray (ArraySegment<object> segment) {
        var result = new object[segment.Count];
        for (int i = 0; i < segment.Count; i++) {
            result[i] = segment.Array[i + segment.Offset];
        }
        return result;
    }

} // Logger class

并且输出有效:

    Project Starting
    someplace.com
    Hello George!
    Hello George!
    200000 Processed
    200000 Processed
    George logged in from someplace.com at 10:30
    George logged in from someplace.com at 10:30

其中一些最终会设置状态栏、标题栏等,这就是它们存在的原因。 (你会在此处还不存在的东西中设置对那些 objects 的引用,我的旧版本会这样做,所以,把它移过去)棘手的部分是 ArraySegment Stuff,也许是一种更简单的方法它,但它足够短并且可以工作,我不在乎。惊讶地发现 ArraySegment 没有 ToArray(),是的,没有看到那个来了。无论如何,谢谢!!!

params 应该可以满足您的需求。您不需要对同一个名称进行多次覆盖。一个就够了。

public static void Warn(string msg, params object[] args) {
  Logger.Log(1, String.Format(msg, args));
}

public static void Error(string msg, params object[] args) {
  Logger.Log(2, String.Format(msg, args));
}

等等。