Переопределение ToString() для List‹MyClass›

У меня есть класс MyClass, и я хотел бы переопределить метод ToString() экземпляров List:

class MyClass
{
    public string Property1 { get; set; }
    public int Property2 { get; set; }
    /* ... */
    public override string ToString()
    {
        return Property1.ToString() + "-" + Property2.ToString();
    }
}

Я хотел бы иметь следующее:

var list = new List<MyClass>
            {
                new MyClass { Property1 = "A", Property2 = 1 },
                new MyClass { Property1 = "Z", Property2 = 2 },
            };

Console.WriteLine(list.ToString());   /* prints: A-1,Z-2 */

Возможно ли это сделать? Или мне пришлось бы создать подкласс List‹MyClass>, чтобы переопределить метод ToString() в моем подклассе? Могу ли я решить эту проблему, используя методы расширения (т. е. можно ли переопределить метод с помощью метода расширения)?

Спасибо!


person Bruno Reis    schedule 27.08.2009    source источник
comment
Для информации: я пытался сделать что-то, связанное с NHibernate, и сопоставление коллекций в сериализованной строке, которая соответствовала бы одному столбцу. Однако я нашел другой способ сделать это, реализовав свой собственный IUserType, и моя реализация создает строку так, как мне нужно! Спасибо всем, кто все же ответил!   -  person Bruno Reis    schedule 27.08.2009


Ответы (7)


Вам понадобится подкласс, чтобы переопределить любой метод. Суть дженериков в том, чтобы сказать, что вы хотите одинакового поведения независимо от типа T. Если вам нужно другое поведение для определенного типа T, вы нарушаете этот контракт и должны будете написать свой собственный класс:

public class MyTypeList : List<MyClass>
{
    public override string ToString()
    {
        return ...
    }
}

Отредактировано, чтобы добавить:

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

public static string ExtendedToString(this List<MyClass> list)
{
     return ....
} 

Используется с

List<MyClass> myClassList = new List<MyClass>
string output = myClassList.ExtendedToString();

Я все еще думаю, что вам лучше создать подкласс...

person Martin Harris    schedule 27.08.2009
comment
Это так мало кода для нового класса списка, поэтому я бы включил его сверху в тот же файл кода, что и класс MyClass. Так что это не очень много дополнительных усилий, чтобы сделать, даже если создание нового класса звучит сложно, на самом деле это не так, потому что большая часть функциональности унаследована от класса List... - person awe; 27.08.2009
comment
О методе расширения: я согласен, что в этом случае вам следует использовать подклассы, потому что метод ToString логически является тем, что вам нужно, а List не имеет очень полезной реализации по умолчанию. Расширение добавит новый метод и оставит метод ToString таким же бесполезным, каким он всегда был для универсального класса List. - person awe; 27.08.2009
comment
можете ли вы уточнить свой подкласс, я использую интерполяцию строк для построения строки в этом методе ToString, например. return (${property1}{property2}), но не распознает свойства? какие-либо предложения - person WhiteSpider; 24.07.2019

Возможно, немного не по теме, но я использую метод расширения ToDelimitedString, который работает для любого IEnumerable<T>. Вы можете (необязательно) указать используемый разделитель и делегат для выполнения пользовательского преобразования строки для каждого элемента:

// if you've already overridden ToString in your MyClass object...
Console.WriteLine(list.ToDelimitedString());
// if you don't have a custom ToString method in your MyClass object...
Console.WriteLine(list.ToDelimitedString(x => x.Property1 + "-" + x.Property2));

// ...

public static class MyExtensionMethods
{
    public static string ToDelimitedString<T>(this IEnumerable<T> source)
    {
        return source.ToDelimitedString(x => x.ToString(),
            CultureInfo.CurrentCulture.TextInfo.ListSeparator);
    }

    public static string ToDelimitedString<T>(
        this IEnumerable<T> source, Func<T, string> converter)
    {
        return source.ToDelimitedString(converter,
            CultureInfo.CurrentCulture.TextInfo.ListSeparator);
    }

    public static string ToDelimitedString<T>(
        this IEnumerable<T> source, string separator)
    {
        return source.ToDelimitedString(x => x.ToString(), separator);
    }

    public static string ToDelimitedString<T>(this IEnumerable<T> source,
        Func<T, string> converter, string separator)
    {
        return string.Join(separator, source.Select(converter).ToArray());
    }
}
person LukeH    schedule 27.08.2009
comment
В целом хорошее предложение: я сам тоже использую такой метод расширения. В зависимости от точного контекста исходного вопроса это может быть или не быть правильным ответом здесь. - person peSHIr; 27.08.2009
comment
Вот некоторые действительно удобные методы расширения, спасибо! Помог мне загрузить внутри функцию, используя IList анонимных типов перечислений, чтобы преобразовать их в список, разделенный запятыми, просто используя (x =› ((short)Convert.ChangeType(x, TypeCode.Int16)) в качестве моего параметра преобразователя. Еще раз спасибо Люк - person dan richardson; 15.06.2010
comment
Согласно комментарию danrichardson, спасибо за вклад, это очень полезно! - person Seb T.; 27.07.2012

На самом деле вы можете использовать трюк с юникодом, чтобы определить альтернативный метод ToString непосредственно для вашего общего списка.

Если вы включите ввод шестнадцатеричных символов в Visual Studio, вы можете создавать невидимые символы, удерживая нажатой клавишу Alt, а затем нажимая следующую команду на цифровой клавиатуре + F F F 9 (теперь отпустите Alt)

Итак, мы можем создать следующую функцию с невидимым символом, расположенным рядом с ее именем... (да, я знаю ее код VB, но концепция все равно будет работать для C#)

<Extension()> _
Public Function ToString(ByVal source As Generic.List(Of Char)) As String
   Return String.Join(separator:="", values:=source.ToArray)
End Function

Теперь в Visual Studio, когда вы получаете доступ к intellisense по своему списку, вы сможете выбирать между стандартной ToString или вашей пользовательской функцией.


Чтобы включить ввод шестнадцатеричных символов в Visual Studio, вам может потребоваться отредактировать реестр.

откройте HKEY_CURRENT_USER\Control Panel\Input Method и создайте REG_SZ с именем EnableHexNumpad, установите для него значение 1

Вам также потребуется отключить ярлыки & для меню «Файл», «Редактировать», «Отладка», «Данные». В Visual Studio откройте меню инструментов, выберите «Настроить», затем откройте вкладку «Команды» и с помощью кнопки выбора «Изменить» для любого элемента меню, который использует любой из символов ABCDEF для его сокращения, удалив &

В противном случае вы будете открывать всплывающие меню вместо ввода шестнадцатеричных символов.

person Richard Lee    schedule 22.10.2014
comment
Не знаю, работает ли это, но звучит гениально и заставило меня хорошенько посмеяться! +1 - person Blake Thingstad; 21.02.2017

Если ваш метод должен называться ToString, вам придется получить класс от List. Вы можете сделать его универсальным:

static class MyList<T> : List<T>
{
    public override string ToString()
    {
        // ...
    }
}

В этом случае вам придется использовать MyList вместо List во всем приложении, если вы хотите иметь собственное преобразование.

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

Вы можете использовать методы расширения, чтобы сделать это более общим:

static class ListExtension
{
    public static void ConvertToString<T>(this IEnumerable<T> items)
    {
        // ...
    }
}

Вы можете использовать его в любом экземпляре IEnumerable<T> так же, как если бы это был обычный метод:

List<MyClass> list = new List<MyClass> { ... };
Console.WriteLine(list.ConvertToString());

int[] array_of_ints = {1,2,3,4,5};
Console.WriteLine(array_of_ints.ConvertToString());
person Bojan Resnik    schedule 27.08.2009

Вам нужно будет создать свой собственный класс, который наследуется от Collection, а затем специально перезаписать метод ToString() этого класса.

person Robban    schedule 27.08.2009

Нет, это невозможно. ToString из TList даст вам строковое представление объекта списка.

Ваши варианты:

  • Выведите из TList и переопределите метод .ToString(), как вы упомянули. (в этом примере я бы не сказал, что это стоит делать)
  • Создайте вспомогательный метод, который преобразует список TList в строку с разделителями-запятыми, например. метод расширения (вероятно, лучшее предложение)
  • Используйте оператор foreach на этапе Console.WriteLine.

Надеюсь, это поможет!

person James    schedule 27.08.2009

В зависимости от точной причины, по которой вы хотите переопределить List<T>.ToString(), чтобы вернуть что-то конкретное, может быть удобно взглянуть на пользовательскую TypeConverter.

Если вы просто хотите, чтобы List<T> определенного T показывало себя определенным образом как string в местах, где используются преобразователи типов, например, в отладчике или в ситуациях типа string.Format("List: {0}", listVariable), этого может быть достаточно.

Возможно, вы просто где-то увидели результат ToString() и захотели его изменить, не зная о существовании TypeConverter и местах, где они используются. Я считаю, что многие/большинство/все (не знаю, какие?) преобразователи типов по умолчанию в .NET Framework просто используют ToString() при преобразовании любого типа, для которого они определены, в string.

person peSHIr    schedule 27.08.2009