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

时间:2017-12-19 16:39:12

标签: c# .net tuples

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个值。然后所有重载将调用主要方法来完成工作。但是,如果主方法接收具有不同数量项的松散类型元组,则如果无法知道元组值的计数,则不清楚如何以通用方式处理所有过载情况。

2 个答案:

答案 0 :(得分:4)

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

答案 1 :(得分:0)

只需使用两个列表,一个类型为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));
}