覆盖 List<MyClass> 的 ToString() 方法

30
我有一个名为MyClass的类,我想要重写List实例的ToString()方法: ```

我有一个名为MyClass的类,我想要重写List实例的ToString()方法:

```
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()方法?我能否使用扩展方法解决此问题(即,是否可以使用扩展方法覆盖方法)?

谢谢!


关于信息:我正在尝试处理与NHibernate相关的事情,即将集合映射到适合单个列的序列化字符串中。然而,我找到了另一种方法来解决它,通过实现自己的IUserType,我的实现按照我需要的方式构造字符串!无论如何,感谢所有回答的人! - Bruno Reis
7个回答

阿里云服务器只需要99元/年,新老用户同享,点击查看详情
32

也许有点跑题,但我使用一个 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());
    }
}

总的来说,这是一个好建议:我自己也使用这样的扩展方法。根据原始问题的确切上下文,这可能或可能不是一个有效的答案。 - peSHIr
这些扩展方法真的非常方便,谢谢!在使用IList匿名枚举类型的函数内将它们转换为逗号分隔列表时帮了我大忙,我只需要将(x => ((short)Convert.ChangeType(x, TypeCode.Int16)) 用作我的转换器参数即可。再次感谢Luke。 - dan richardson
根据danrichardson的评论,感谢您的输入,这非常有用! - Seb T.
使用 System; 使用 System.Linq; 使用 System.Globalization; - Emerson

27

你需要创建子类来重写任何方法。泛型的目的是说你想要相同的行为,不论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();
我仍然认为你最好是通过子类化来实现...

1
这是一个新列表类的很少的代码,所以我会把它放在同一个代码文件中MyClass类的顶部。因此,即使创建一个新类听起来很复杂,但实际上并不需要太多额外的努力,因为大部分功能都是从List类继承的... - awe
关于扩展方法: 我同意在这种情况下你应该选择子类化,因为ToString方法在逻辑上是你想要的,而List没有非常有用的默认实现。扩展会添加一个新方法,并且将ToString方法保留在那里,就像它一直对于通用的List类一样没用。 - awe
你能详细说明一下你的子类吗?我在 ToString 方法中使用字符串插值来构建一个字符串,例如返回 ($"{property1}{property2}"),但它无法识别这些属性。有什么建议吗? - WhiteSpider
这有点晚了,但关于WhiteSpider的评论 - return string.Join(", ", this);将产生原始要求,或者如果您想要更自定义的内容 - return string.Join(", ", this.Select(entry => $"{entry.someProperty} some formatting {entry.otherproperty}"); - undefined

3

您可以使用Unicode技巧,直接针对您的通用列表定义替代ToString方法。

如果您启用Visual Studio中的十六进制字符输入,则可以通过按住Alt键,然后在数字键盘上按以下内容+FFF9(现在释放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中,当您访问智能感知以针对您的列表时,您将能够在标准ToString和自定义函数之间进行选择。
为了在Visual Studio中启用十六进制字符输入,您可能需要编辑注册表。 打开HKEY_CURRENT_USER\Control Panel\Input Method,并创建一个名为EnableHexNumpad的REG_SZ,将其设置为1。 您还需要禁用文件、编辑、调试和数据菜单的&键盘快捷方式。在Visual Studio中,打开“工具”菜单,选择“自定义”,然后打开“命令”选项卡,并使用修改选择按钮来删除任何使用ABCDEF字符作为其快捷方式的菜单项的&。 否则,您将打开弹出式菜单,而不是键入十六进制字符。

2
不知道它是否有效,但听起来很巧妙,让我忍不住笑了!+1 - Blake Thingstad

2
如果你的方法必须命名为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());

0
你需要创建一个自定义类,该类继承自Collection,然后重写该类的ToString()方法。

0

不,这是不可能的。TList的ToString将给出列表对象的字符串表示形式。

您的选项包括:

  • 从TList派生并覆盖.ToString()方法,就像您提到的那样。(在这个例子中,我认为这样做不值得)
  • 创建一个帮助方法,将TList列表转换为逗号分隔的字符串,例如扩展方法(可能是最好的建议)
  • 在Console.WriteLine阶段使用foreach语句。

希望能有所帮助!


0

根据您想要覆盖 List<T>.ToString() 并返回特定内容的原因,可能需要查看自定义 TypeConverter 实现。

如果您只是想让特定类型的 List<T> 在使用 TypeConverters 的位置(例如调试器或 string.Format("List: {0}", listVariable) 类型的情况下)以某种方式显示为 string,那么这可能已经足够了。

您可能只是在某个地方看到了 ToString() 的结果并希望更改它,而不知道 TypeConverter 的存在和它们被使用的位置。我相信 .NET Framework 中的许多/大多数/所有(不确定哪些?)默认 TypeConverters 只是在将任何定义了其类型的类型转换为 string 时使用 ToString()。


网页内容由stack overflow 提供, 点击上面的
可以查看英文原文,