30

I have a class MyClass, and I would like to override the method ToString() of instances of List:

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

I would like to have the following:

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

Is it possible to do so? Or I would have to subclass List<MyClass> to override the method ToString() in my subclass? Can I solve this problem using extension methods (ie, is it possible to override a method with an extension method)?

Thanks!

Bruno Reis
  • 37,201
  • 11
  • 119
  • 156
  • For information: I was trying to do something related to NHibernate, and the mapping of collections in a serialized string that would fit a single column. However I found another way to do it, by implement my own IUserType, and my implementation constructs the string as I need! Thanks everyone who answered anyway! – Bruno Reis Aug 27 '09 at 16:25

7 Answers7

32

Perhaps a bit off-topic, but I use a ToDelimitedString extension method which works for any IEnumerable<T>. You can (optionally) specify the delimiter to use and a delegate to perform a custom string conversion for each element:

// 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());
    }
}
LukeH
  • 263,068
  • 57
  • 365
  • 409
  • Good suggestion in general: I use such an extension method myself too. Depending on the exact context of the original question this might or might not be a valid answer here. – peSHIr Aug 27 '09 at 11:55
  • These are some really handy extension methods, thanks! Helped me loads inside a function using an IList of anonymous enum types to convert them to a comma seperated list, just used (x => ((short)Convert.ChangeType(x, TypeCode.Int16)) as my convertor parameter. Thanks again Luke – dan richardson Jun 15 '10 at 11:30
  • As per danrichardson comment, thanks for the input this is quite useful ! – Seb T. Jul 27 '12 at 10:58
  • using System; using System.Linq; using System.Globalization; – Emerson Sep 21 '22 at 00:10
27

You'll need to subclass to override any method. The point of generics is to say that you want the same behaviour regardless of the type of T. If you want different behaviour for a specific type of T then you are breaking that contract and will need to write your own class:

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

Edited to add:

No, you can't override a method by creating an extension, but you could create a new method with a different signature that is specific to this list type:

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

Used with

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

I still think you're better off subclassing though...

Martin Harris
  • 28,277
  • 7
  • 90
  • 101
  • 1
    This is so little code for the new list class, so I would include it on top in the same code file as the `MyClass` class. So this is not very much extra effort to do do, even if it sounds complicated to create a new class, it really isn't because most of the functionality is inherited from the `List` class... – awe Aug 27 '09 at 10:31
  • **About the extention method:** I agree that in this case you should go with the subclassing, because the `ToString` method is logically what you want, and `List` does not have very useful default implementation of it. Extension would add a new method and leave the `ToString` method there just as useless as it has allways been for the generic `List` class. – awe Aug 27 '09 at 10:46
  • can you elaborate on your subclass, I use string interpolation to build a string in that ToString method eg. return ($"{property1}{property2}"), but it doesn't recognize the properties ? any suggestions – WhiteSpider Jul 24 '19 at 08:06
3

You can actually use a unicode trick to allow you to define an alternate ToString method directly against your generic list.

If you enable hex character input into visual studio then you can create invisible characters by holding down the Alt key, then pressing the following on your numeric keypad + F F F 9 (now release Alt)

So we can create the following function with an invisible character placed next to its name... (yes i know its VB code, but the concept will still work work for C#)

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

Now in visual studio, when you access intellisense against your list, you will be able to choose between either the standard ToString or your custom function.


To enable hex character input into visual studio you may need to edit your registry

open HKEY_CURRENT_USER\Control Panel\Input Method and create a REG_SZ called EnableHexNumpad set this to 1

You will also need to disable the & shortcuts for the File, Edit, Debug, Data menus, In visual studio, open the tools menu, select customize, then open the commands tab, and using the modify selection button for any menu item that uses either of the ABCDEF charactes for its short cut, by removing the &

Otherwise you will end up opening popup menus, instead of typing hex characters.

2

If you method must be named ToString you will have to derive a class from List. You can make it a generic:

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

In this case, you would have to use MyList instead of List throughout your application if you wish to have your custom conversion.

However, if you can choose a different name for your method, you can use extension methods and achieve the same effect, with almost no modifications to your code:

You can use extension methods to make this more generic:

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

You can use it on any instance of IEnumerable<T> just as if it were an ordinary method:

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());
Bojan Resnik
  • 7,320
  • 28
  • 29
0

You would have to create your own custom class that inherits from Collection and then overwride the ToString() method of that class specifically.

Robban
  • 6,729
  • 2
  • 39
  • 47
0

No its not possible. ToString of TList will give you the string representation of the list object.

Your options are:

  • Derive from TList and override the .ToString() method as you mentioned. (in this example I wouldn't say its worth doing so)
  • Create a helper method that converts a TList list to a comma delimited string e.g. extension method (probably best suggestion)
  • Use a foreach statement at the Console.WriteLine stage.

Hope that helps!

James
  • 80,725
  • 18
  • 167
  • 237
0

Depending on the exact reason you have for wanting to override List<T>.ToString() to return something specific it might be handy to have a look at custom TypeConverter implementations.

If you simply want a List<T> of specific T to show itself a certain way as a string in locations where TypeConverters are used, like in the debugger or in string.Format("List: {0}", listVariable) type situations, this might be enough.

You might just have seen the result of ToString() being shown somewhere and wanted to change that, without knowing about the existence of TypeConverter and locations where they are used. I believe many/most/all (not sure which?) of the default TypeConverters in the .NET Framework simply use ToString() when converting any type for which they are defined for to a string.

peSHIr
  • 6,279
  • 1
  • 34
  • 46