【问题标题】:Restrict generic extension method from extending strings限制泛型扩展方法扩展字符串
【发布时间】:2017-04-17 04:21:04
【问题描述】:

我有一个非常通用的扩展方法可以在控制台中显示任何类型的列表:

public static void ShowList<T>(this IEnumerable<T> Values)
{
    foreach (T item in Values)
    {
        Console.WriteLine(item);
    }
}

当我有string 时,我不能使用这个方法

string text = "test";
text.ShowList();

但对于string,它在我的应用程序中没有意义。

我怎样才能从这个方法中排除string?我读过一些关于

ShowList<T>(this IEnumerable<T> Values): Where != string //doesn't work

【问题讨论】:

  • string 的哪些方面使其不合适?你想让new List&lt;char&gt; { 'x', 'y', 'z' }.ShowList() 工作吗?
  • 为什么?两者都是字符序列。你需要考虑是什么让字符串对你来说显然“特别”。
  • 哦,它在很多方面都很特别——但不是就它是否可以被视为可读的字符序列而言。
  • 顺便说一句,我鼓励您遵循 .NET 命名约定 - Values 应该是 values。 (您可能也应该验证它不为空。)
  • @Toshi 1985 年的 C++ 被调用,他们希望他们的 char[]s 回来 ;)

标签: c# methods extension-methods


【解决方案1】:

您可以创建另一个特定于stringShowList() 重载并将其标记为[Obsolete]

[Obsolete("Not intended for strings", true)]
public static void ShowList(this string val)
{
}

通过将true 传递给IsError 参数,编译器将阻止您使用该方法。

ObsoleteAttribute

【讨论】:

    【解决方案2】:

    这感觉有点奇怪,老实说——如果某些东西应该适用于任何字符序列,那么它应该适用于字符串,字符。

    如果你真的想让它编译失败,你可以添加一个接受string的重载,它被标记为过时:

    [EditorBrowsable(EditorBrowsableState.Never)]
    [Obsolete(IsError = true, Message = "A string is a sequence of characters, but is not intended to be shown as a list")]
    public static void ShowList(this string text)
    {
        throw new NotSupportedException();
    }
    

    重载解析将选择该方法,然后编译失败。 EditorBrowsable 属性希望从 Intellisense 中删除它 - 但你必须看看它是否真的有效。 (它可能仍然显示另一个重载,即使它不会被选中。)

    另一种选择是实现ShowList&lt;T&gt;,就好像字符串是一个单项列表:

    // Specialization to avoid listing each character separately.
    public static void ShowList(this string text) => new[] { text }.ShowList();
    

    换句话说,让调用有效,但处理得更恰当。

    【讨论】:

    • 将其添加到我的代码后,ShowList 仍显示在自动完成中。有没有办法抑制这种行为?
    • @Toshi:EditorBrowsable 可能会有所帮助 - 我已将其添加到我的答案中。
    • 我宁愿选择NotSupportedException,因为这种方法不起作用是设计使然,而不是因为你没有时间去实现它。
    • @CodesInChaos:是的,那可能会更好——会编辑。
    【解决方案3】:

    对于静默忽略,您可以使用:

    public static void ShowList<T>(this IEnumerable<T> Values)
    {
        if (Values is String) return;
        // ...
    }
    

    或者如果你还想写字符串:

    public static void ShowList<T>(this IEnumerable<T> Values)
    {
        if (Values is String)
            Console.WriteLine(Values);
        else
            foreach (T item in Values)
            {
                Console.WriteLine(item);
            }
    }
    

    【讨论】:

    • 请注意,这会影响"abc".AsEnumerable().ShowList()。目前尚不清楚 OP 想要什么或 OP 是否考虑到这一点。
    • 不,我的 2 种方法使 no 有所不同:"abc".AsEnumerable() 只是对已经是 IEnumerable&lt;T&gt; 的所有内容执行 return source;,其中包括字符串。被调用的方法具有签名AsEnumerable&lt;TSource&gt;(this IEnumerable&lt;TSource&gt; source),您可以在此处查看其实现:github.com/Microsoft/referencesource/blob/master/System.Core/…(我们所说的第 823 行)
    • 这就是我的意思:你选择Console.WriteLine(Values); 实现是因为Values is String 将是true,所以你给它特殊的string 处理,即使调用者试图绕过它特殊待遇。对于由编译时类型决定调用哪个实现的其他答案,将使用IEnumerable&lt;char&gt; 版本。
    猜你喜欢
    • 2023-03-20
    • 1970-01-01
    • 2015-07-19
    • 1970-01-01
    • 2015-02-26
    • 1970-01-01
    • 1970-01-01
    • 1970-01-01
    相关资源
    最近更新 更多