C#方法可以定义可变数量的对象参数,以及整数参数上的变量数吗?

Posted

tags:

篇首语:本文由小常识网(cha138.com)小编为大家整理,主要介绍了C#方法可以定义可变数量的对象参数,以及整数参数上的变量数吗?相关的知识,希望对你有一定的参考价值。

C#方法可以用这种简单的方式定义可变数量的对象参数和整数参数的变量数,还是为调用者定义一些其他简单的方法?

什么是简单的意思? 从调用者的角度来看很简单。优先考虑所需的最少量的打字/字符,并且易于理解和使用。

出于什么目的? 动机是Console.WriteLine的基本包装,可以更容易地输出圆柱文本。现有的符号不直观(例如,它需要负数)。对于某些基本场景,它也比需要的更冗长。

一个似乎理想的答案,如果可能的话,将展示如何使用两个元组定义这样的方法,每个元组具有n个值(或者嘿,最多可以说是10个值)。我们的想法是它可以减少调用者代码:

// Output items given column widths
Console.WriteCols(("Name", "Address", "Age"), (20, 10, 30))
Console.WriteCols(("John", "123 Street", 28), (20, 10, 30))
Console.WriteCols(("Mary", "456 Street"), (20, 10))

我知道它只是一个值列表,但这适合我们的大多数列用例,而且,如果第一个例子是可能的,似乎可以在以后需要时添加字符串变量,例如:

Console.WriteCols(("{0} John", "123 Street", 28), (20, 10, 30), ("Dr."))

好的去做,有什么问题? 对于第一个基本示例,我尝试了一些方法。一个棘手的问题是我没有办法计算元组的值。也许它正在盯着我,或者说我已经过于复杂了。第一个想法是创建具有2个元组的方法重载,每个元组最多允许10个值。然后所有重载将调用主要方法来完成工作。但是,如果主方法接收具有不同数量项的松散类型元组,则如果无法知道元组值的计数,则不清楚如何以通用方式处理所有过载情况。

答案
public static class MyConsole
{
    public static void Test()
    {
        Console.Out
            .Columns(10, 40, 60)
            .WriteLine("foo", "bar", "baz")
            .WriteLine("LOL", "WUT", "BBQ")
            .WriteLine("HA", "ha", "ha");

        var cols = Console.Error.Columns(10, 40, 30);

        cols.WriteLine("Mary", "Had", "a");
        cols.WriteLine("Little", "lamb", "it's");
    }

    public static ColumnWriter Columns(params int[] widths) => new ColumnWriter(Console.Out, widths);
    public static ColumnWriter Columns(this TextWriter writer, params int[] widths) => new ColumnWriter(writer, widths);

    public class ColumnWriter
    {
        public ColumnWriter(TextWriter writer, int[] widths)
        {
            Debug.Assert(writer != null);
            Debug.Assert(widths != null);

            _writer = writer;
            _widths = widths;
        }
        private TextWriter _writer;
        private int[] _widths;

        public ColumnWriter Line()
        {
            _writer.WriteLine();
            return this;
        }

        public ColumnWriter Write(params object[] args)
        {
            Debug.Assert(args.Length == _widths.Length);

            var count = Math.Min(_widths.Length, args.Length);
            for (int idx = 0; idx < count; ++idx)
            {
                var fmt = "{0," + _widths[idx] + "}";
                _writer.Write(fmt, args[idx]);
            }

            return this;
        }
        public ColumnWriter WriteLine(params object[] args)
        {
            return Write(args).Line();
        }
    }
}

这可以通过为Columns(string formatString)Columns(this TextWriter writer, string formatString)添加重载来改善。


坏主意

Columns()可以返回一个委托,对于一些看起来奇怪的语法(感谢Evk向我展示方式 - 并且不喜欢语法比我更好)。更糟糕的是,我们可以给它多个索引器重载。

在任何一种情况下我都不喜欢这种新颖的语法。这是有效的,但任何在生产代码中使用任何一个的人都会偷羊:

public static class MyConsole
{
    public static void Test()
    {
        //  Not a great idea.
        MyConsole.WriteLines(10, 10)("foo", "bar")("baz", "planxty");

        //  Truly awful idea.
        MyConsole.Columns(10, 10)["foo", "bar"]["baz", "planxty"].End();
    }

    public static ColumnWriter Columns(params int[] widths) => new ColumnWriter(widths);

    #region Not a great idea
    //  Evk showed me how to make this work. 
    public delegate Params Params(params object[] values);
    public static Params WriteLines(params int[] widths)
    {
        var writer = new ColumnWriter(widths);

        return new Params(writer.WriteLineParams);
    }
    #endregion Not a great idea

    public class ColumnWriter
    {
        public ColumnWriter(int[] widths)
        {
            _widths = widths;
        }
        private int[] _widths;

        #region Truly awful idea.
        public ColumnWriter this[object o] => WriteLine(o);
        public ColumnWriter this[object o1, object o2] => WriteLine(o1, o2);
        public ColumnWriter this[object o1, object o2, object o3] => WriteLine(o1, o2, o3);

        //  ...moar overloards...

        //  In C# x[0]; is an expression, not a statement. 
        //  x[0].End(); is a statement. Horrible, most horrible. 
        //  Maybe I should name it FireMe() instead of End()
        public void End() { }
        #endregion Truly awful idea.

        public ColumnWriter Line()
        {
            Console.WriteLine();
            return this;
        }

        public ColumnWriter Write(params object[] args)
        {
            var count = Math.Min(_widths.Length, args.Length);
            for (int idx = 0; idx < count; ++idx)
            {
                var fmt = "{0," + _widths[idx] + "}";
                Console.Write(fmt, args[idx]);
            }

            return this;
        }
        public ColumnWriter WriteLine(params object[] args) 
            => Write(args).Line();

        #region Not a great idea
        public Params WriteLineParams(params object[] args)
        {
            WriteLine(args);

            return WriteLineParams;
        }
        #endregion Not a great idea
    }
}
另一答案

只需使用两个列表,一个是string类型,另一个是int类型。对于其他参数(例如格式化程序),您可以添加params关键字:

WriteCols(List<string> cols, List<int> width, params string[] formatters)
{
    if(cols.Count != widths.Count) throw new ArgumentException("Paramater-counts not matching");

    foreach(var e in cols)
        Console.Write(/* add some padding so that every column has the desired width */ String.Format(e, formatters));
}

以上是关于C#方法可以定义可变数量的对象参数,以及整数参数上的变量数吗?的主要内容,如果未能解决你的问题,请参考以下文章

C# 是不是支持可变数量的参数,以及如何支持?

Python C++ API - 具有可变数量参数的重载函数

[C++11 模板的改进] --- 可变参数模板

如何制作可变参数宏(可变数量的参数)

C语言基础:可变参数

27.可变参数