簡體   English   中英

C#方法可以定義可變數目的對象參數,以及可變數目的整數參數嗎?

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

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

暫無
暫無

聲明:本站的技術帖子網頁,遵循CC BY-SA 4.0協議,如果您需要轉載,請注明本站網址或者原文地址。任何問題請咨詢:yoyou2525@163.com.

 
粵ICP備18138465號  © 2020-2024 STACKOOM.COM