Console.WriteLine и общий список

Я часто ловлю себя на том, что пишу такой код:

List<int> list = new List<int> { 1, 3, 5 };
foreach (int i in list) {
    Console.Write("{0}\t", i.ToString()); }
Console.WriteLine();

Лучше бы что-то вроде этого:

List<int> list = new List<int> { 1, 3, 5 };
Console.WriteLine("{0}\t", list);

Я подозреваю, что есть какой-то умный способ сделать это, но я его не вижу. У кого-нибудь есть лучшее решение, чем первый блок?


person Justin R.    schedule 09.09.2008    source источник


Ответы (7)


Сделай это:

list.ForEach(i => Console.Write("{0}\t", i));

РЕДАКТИРОВАТЬ: Для других, которые ответили - он хочет, чтобы все они были в одной строке, с вкладками между ними. :)

person Jason Bunting    schedule 09.09.2008
comment
Если в вашей среде нет ForEach (как у меня), вы можете реализовать свой собственный. - person testing; 25.02.2019

Другой подход, просто для удовольствия:

Console.WriteLine(string.Join("\t", list));
person Vinko Vrsalovic    schedule 09.09.2008
comment
У меня это не работает с List‹int›. Я получил InvalidCastException. - person Setyo N; 28.06.2013
comment
Я также получил InvalidCastException - ответ неверный. Более того, тот же результат, когда я пытался использовать List<CustomType> с неявным и явным преобразованием, реализованным для CustomType - person sergtk; 19.12.2013
comment
Вам не нужно преобразовывать список в строку и преобразовывать в массив. string.Join примет список, так как есть перегрузка, которая принимает параметр IEnumerable‹T›. как в IEnumerable (List) of ‹T› (в данном случае T = int), поэтому List будет повторяться и int.ToString( ) будет вызываться в методе Join(). Console.WriteLine(string.Join(\t, список)); docs.microsoft.com/en- нас/dotnet/api/ - person Antony Booth; 22.05.2019

Если есть фрагмент кода, который вы все время повторяете в соответствии с правилом «Не повторяйтесь», вы должны поместить его в свою собственную библиотеку и вызвать его. Имея это в виду, есть 2 аспекта получения правильного ответа здесь. Во-первых, это ясность и краткость кода, вызывающего библиотечную функцию. Во-вторых, влияние foreach на производительность.

Сначала давайте подумаем о ясности и краткости вызывающего кода.

Вы можете сделать foreach несколькими способами:

  1. для цикла
  2. цикл foreach
  3. Коллекция.ForEach

Из всех способов сделать foreach List.ForEach с лямбой — самый понятный и краткий.

list.ForEach(i => Console.Write("{0}\t", i));

Итак, на данном этапе может показаться, что List.ForEach — это то, что нужно. Однако какова производительность этого? Это правда, что в этом случае время записи в консоль будет определять производительность кода. Когда мы что-то знаем о производительности той или иной языковой функции, мы, конечно же, должны хотя бы подумать об этом.

Согласно измерению производительности foreach, проведенному Дастоном Кэмпбеллом, самый быстрый способ перебора списка в оптимизированный код использует цикл for без вызова List.Count.

Однако цикл for является многословной конструкцией. Это также рассматривается как очень итеративный способ делать вещи, которые не соответствуют текущей тенденции к функциональным идиомам.

Так можем ли мы добиться краткости, ясности и производительности? Мы можем, используя метод расширения. В идеальном мире мы бы создали метод расширения на консоли, который берет список и записывает его с разделителем. Мы не можем этого сделать, потому что Console — это статический класс, а методы расширения работают только с экземплярами классов. Вместо этого нам нужно поместить метод расширения в сам список (согласно предложению Дэвида Б.):

public static void WriteLine(this List<int> theList)
{
  foreach (int i in list)
  {
    Console.Write("{0}\t", t.ToString());
  }
  Console.WriteLine();
}

Этот код будет использоваться во многих местах, поэтому мы должны внести следующие улучшения:

  • Вместо использования foreach мы должны использовать самый быстрый способ итерации коллекции, который представляет собой цикл for с кешированным счетчиком.
  • В настоящее время в качестве аргумента может быть передан только список. В качестве библиотечной функции мы можем обобщить ее, приложив небольшие усилия.
  • Использование списка ограничивает нас только списками, использование IList позволяет этому коду работать и с массивами.
  • Поскольку метод расширения будет в IList, нам нужно изменить имя, чтобы было понятнее, что мы пишем:

Вот как будет выглядеть код функции:

public static void WriteToConsole<T>(this IList<T> collection)
{
    int count = collection.Count();
    for(int i = 0;  i < count; ++i)
    {
        Console.Write("{0}\t", collection[i].ToString(), delimiter);
    }
    Console.WriteLine();
}

Мы можем улучшить это еще больше, разрешив клиенту передавать разделитель. Затем мы могли бы предоставить вторую функцию, которая записывает в консоль стандартный разделитель, например:

public static void WriteToConsole<T>(this IList<T> collection)
{
    WriteToConsole<T>(collection, "\t");
}

public static void WriteToConsole<T>(this IList<T> collection, string delimiter)
{
    int count = collection.Count();
    for(int i = 0;  i < count; ++i)
    {
         Console.Write("{0}{1}", collection[i].ToString(), delimiter);
    }
    Console.WriteLine();
}

Итак, теперь, учитывая, что нам нужен краткий, четкий и эффективный способ записи списков в консоль, у нас он есть. Вот весь исходный код, включая демонстрацию использования библиотечной функции:

using System;
using System.Collections.Generic;
using System.Linq;

namespace ConsoleWritelineTest
{
    public static class Extensions
    {
        public static void WriteToConsole<T>(this IList<T> collection)
        {
            WriteToConsole<T>(collection, "\t");
        }

        public static void WriteToConsole<T>(this IList<T> collection, string delimiter)
        {
            int count = collection.Count();
            for(int i = 0;  i < count; ++i)
            {
                Console.Write("{0}{1}", collection[i].ToString(), delimiter);
            }
            Console.WriteLine();
        }
    }

    internal class Foo
    {
        override public string ToString()
        {
            return "FooClass";
        }
    }

    internal class Program
    {

        static void Main(string[] args)
        {
            var myIntList = new List<int> {1, 2, 3, 4, 5};
            var myDoubleList = new List<double> {1.1, 2.2, 3.3, 4.4};
            var myDoubleArray = new Double[] {12.3, 12.4, 12.5, 12.6};
            var myFooList = new List<Foo> {new Foo(), new Foo(), new Foo()};
            // Using the standard delimiter /t
            myIntList.WriteToConsole();
            myDoubleList.WriteToConsole();
            myDoubleArray.WriteToConsole();
            myFooList.WriteToConsole();
            // Using our own delimiter ~
            myIntList.WriteToConsole("~");
            Console.Read();
        }
    }
}

=======================================================

Вы можете подумать, что это должен быть конец ответа. Однако можно сделать еще одно обобщение. Из вопроса Fatcat не ясно, всегда ли он пишет в консоль. Возможно, что-то еще нужно сделать в foreach. В этом случае ответ Джейсона Бантинга даст эту общность. Вот еще раз его ответ:

list.ForEach(i => Console.Write("{0}\t", i));

Это если мы не внесем еще одно уточнение в наши методы расширения и не добавим FastForEach, как показано ниже:

public static void FastForEach<T>(this IList<T> collection, Action<T> actionToPerform)
    {
        int count = collection.Count();
        for (int i = 0; i < count; ++i)
        {
            actionToPerform(collection[i]);    
        }
        Console.WriteLine();
    }

Это позволяет нам выполнять любой произвольный код для каждого элемента в коллекции, используя максимально быстрый метод итерации.

Мы даже можем изменить функцию WriteToConsole, чтобы она использовала FastForEach.

public static void WriteToConsole<T>(this IList<T> collection, string delimiter)
{
     collection.FastForEach(item => Console.Write("{0}{1}", item.ToString(), delimiter));
}

Итак, теперь весь исходный код, включая пример использования FastForEach:

using System;
using System.Collections.Generic;
using System.Linq;

namespace ConsoleWritelineTest
{
    public static class Extensions
    {
        public static void WriteToConsole<T>(this IList<T> collection)
        {
            WriteToConsole<T>(collection, "\t");
        }

        public static void WriteToConsole<T>(this IList<T> collection, string delimiter)
        {
             collection.FastForEach(item => Console.Write("{0}{1}", item.ToString(), delimiter));
        }

        public static void FastForEach<T>(this IList<T> collection, Action<T> actionToPerform)
        {
            int count = collection.Count();
            for (int i = 0; i < count; ++i)
            {
                actionToPerform(collection[i]);    
            }
            Console.WriteLine();
        }
    }

    internal class Foo
    {
        override public string ToString()
        {
            return "FooClass";
        }
    }

    internal class Program
    {

        static void Main(string[] args)
        {
            var myIntList = new List<int> {1, 2, 3, 4, 5};
            var myDoubleList = new List<double> {1.1, 2.2, 3.3, 4.4};
            var myDoubleArray = new Double[] {12.3, 12.4, 12.5, 12.6};
            var myFooList = new List<Foo> {new Foo(), new Foo(), new Foo()};

            // Using the standard delimiter /t
            myIntList.WriteToConsole();
            myDoubleList.WriteToConsole();
            myDoubleArray.WriteToConsole();
            myFooList.WriteToConsole();

            // Using our own delimiter ~
            myIntList.WriteToConsole("~");

            // What if we want to write them to separate lines?
            myIntList.FastForEach(item => Console.WriteLine(item.ToString()));
            Console.Read();
        }
    }
}
person Ed Sykes    schedule 04.06.2009
comment
Это действительно хороший ответ! Хотя я вижу, что мне не нравится идея иметь метод расширения, такой как WriteToConsole, в общем списке. Это явно нарушает схему единой ответственности. Я бы не ожидал, что список будет иметь какие-либо знания о консоли, будь то ее реализация или метод расширения. - person Florian Greinacher; 29.09.2011
comment
правда, я думаю, ваш выбор зависит от того, что вам нравится и какая забота больше ... - person Ed Sykes; 07.08.2012

List<int> list = new List<int> { 1, 3, 5 };
list.ForEach(x => Console.WriteLine(x));

Редактировать: Черт возьми! потребовалось слишком много времени, чтобы открыть визуальную студию, чтобы протестировать ее.

person George Mauer    schedule 09.09.2008
comment
Вы должны быть относительно младшим - кому нужна Visual Studio? У меня в голове работает CLR. Дж/К - person Jason Bunting; 10.09.2008

        List<int> a = new List<int>() { 1, 2, 3, 4, 5 };
        a.ForEach(p => Console.WriteLine(p));

edit: аааа, он меня опередил.

person John Boker    schedule 09.09.2008

Также вы можете присоединиться:

var qwe = new List<int> {5, 2, 3, 8};
Console.WriteLine(string.Join("\t", qwe));
person Lev Lukomsky    schedule 06.03.2013

public static void WriteLine(this List<int> theList)
{
  foreach (int i in list)
  {
    Console.Write("{0}\t", t.ToString());
  }
  Console.WriteLine();
}

Тогда позже...

list.WriteLine();
person Amy B    schedule 15.09.2008