简体   繁体   English

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

[英]Can a C# method define a variable number of object parameters, and a variable number on integer parameters?

Can a C# method define a variable number of object parameters, and a variable number on integer parameters, in this simple way, or some other simple way for the caller? C#方法能否以这种简单方式或调用方的其他一些简单方式定义可变数量的对象参数和整数参数上的变量?

What does simple mean? 简单是什么意思?
Simple as seen from the callers perspective. 从呼叫者的角度来看很简单。 Prioritizing least amount typing/characters required, and easy to understand and use. 优先考虑最少需要的键入/字符,并且易于理解和使用。

For what purpose? 出于什么目的?
The motivation is a basic wrapper for Console.WriteLine that makes it easier to output columned text. 动机是Console.WriteLine的基本包装,它使输出列文本更加容易。 The existing notation is not intuitive (for ex, it requires negative numbers). 现有的符号不直观(例如,它需要负数)。 It's also more verbose than needed for some basic scenarios. 它也比某些基本方案所需要的更为冗长。

One answer that seems ideal, if it's possible, would show how to define a method like this using two tuples, each tuple with n values (or hey, just up to say, 10 values). 如果可能的话,一个似乎很理想的答案将显示如何使用两个元组来定义这样的方法,每个元组具有n个值(或者嘿,最多可以说10个值)。 The thinking is it could reduce the caller code to this: 这种想法是可以将调用者代码简化为:

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


I know it's just a list of values, but that would fit most of our column use cases, and moreover, if the first example is possible, it seems string variables could be added later if needed, like: 我知道这只是一个值列表,但这将适合我们的大多数列用例,而且,如果可以使用第一个示例,则似乎可以在以后根据需要添加字符串变量,例如:

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

Fine go do it, what's the problem? 好吧,这是什么问题?
For the first basic example, I've tried a few approaches. 对于第一个基本示例,我尝试了几种方法。 One sticking point is I don't see a way to get a count of values for a Tuple. 症结之一是我看不到一种获取元组值计数的方法。 Maybe it's staring me in the face, or I'm over complicating this. 也许是盯着我看,或者让我太复杂了。 The first idea was to just create method overloads with 2 Tuples that allow up to 10 values for each tuple. 第一个想法是只创建带有2个元组的方法重载,每个元组最多允许10个值。 Then all overloads would call a primary method to do the work. 然后,所有重载都将调用主要方法来完成工作。 However, if the primary method is receiving loosely typed tuples with varying numbers of items, it was not clear how to handle all the overload cases in a generalized way, if the count of tuple values can't be known. 但是,如果主要方法是接收具有不同数量项的松散类型的元组,那么如果不知道元组值的计数,则不清楚如何以通用方式处理所有重载情况。

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

This could be improved by adding overloads for Columns(string formatString) and Columns(this TextWriter writer, string formatString) . 这可以通过为Columns(string formatString)Columns(this TextWriter writer, string formatString)添加重载来改善。


Bad ideas 坏主意

Columns() can be made to return a delegate, for some odd looking syntax (thanks Evk for showing me the way -- and for not liking the syntax any better than I do). 可以使Columns()返回委托,以获取某种奇怪的语法(感谢Evk向我展示了这种方式-并且没有比我更喜欢这种语法)。 Worse yet, we can give it multiple indexer overloads. 更糟糕的是,我们可以给它提供多个索引器重载。

I don't like the novel syntax in either case. 无论哪种情况,我都不喜欢新颖的语法。 This works, but anybody who'd use either one in production code would steal sheep: 这可行,但是任何在生产代码中使用其中一个的人都会偷羊:

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

Just use two lists, one of type string and a second of type int . 只需使用两个列表,一个列表为string类型,第二个为int类型。 For further parameters (eg formatters) you can then add the params keyword: 对于其他参数(例如格式化程序),您可以添加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));
}

声明:本站的技术帖子网页,遵循CC BY-SA 4.0协议,如果您需要转载,请注明本站网址或者原文地址。任何问题请咨询:yoyou2525@163.com.

 
粤ICP备18138465号  © 2020-2024 STACKOOM.COM