首页 > 解决方案 > 在 C# 中是否有更聪明的方法来重载一组具有不同名称和多个签名的方法?

问题描述

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

我正在寻找是否有一种“更智能”的方法来做到这一点,然后一遍又一遍地编写同一方法的每个版本的每个重载方法。感觉就是这样,容易出错。

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

        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");

现在,我知道我可以为同一例程的每个版本编写每个重载方法(请记住,每个版本的消息格式都会略有不同),并且我可以在其上添加某种“代码”,例如:

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

...和 ​​Log.Error 是某种枚举或任何东西,但这很讨厌。

我想做的就是将这一切都归结为一个 case 语句,该语句将处理格式,然后将其输入到输出中。我需要编写的大部分代码都是重载中继,99% 重复相同的事情。必须有更聪明的方法。

我已经在其他语言中看到了一些可能会这样做的东西,但如果可能的话,我正在寻找一种 C# (.Net Framework 4.8) 方法来做到这一点。除了全部写出来之外,“最干净”和“更聪明”的做法是什么?

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

- 编辑

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

    ...

    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);
    }

    ...

我想看看我是否可以摆脱那个“类型”参数并使用函数的名称来转发它。也许那是某种模板或重载。我想我可能已经从 Console.WriteLine 中进行了一些复制/粘贴(已经有一段时间了),或者至少将它用作起点。

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

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

    ...

        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);

现在,我还没有我的格式的东西,(不相关,但可以使用该值,它将进入控制台日志窗口,因此它们将被着色并且各种图章将包装它们,但这是简单的部分为以后。)

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

并且输出有效:

    项目启动
    某地.com
    你好乔治!
    你好乔治!
    200000 已处理
    200000 已处理
    George 于 10:30 从 someplace.com 登录
    George 于 10:30 从 someplace.com 登录

其中一些最终会设置状态栏、标题栏等,这就是它们存在的原因。(您会在此处尚不存在的东西中设置对这些对象的引用,我的旧对象会这样做,因此,将其移过来)棘手的部分是 ArraySegment Stuff,也许是一种更简单的方法,但它是足够短并且我不在乎的作品。惊讶地发现 ArraySegment 没有 ToArray(),是的,没有看到那个。总之感谢!!!

标签: c#classdesign-patternsmethodsoverloading

解决方案


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));
}

等等。


推荐阅读