【问题标题】:StringBuilder Extension Append Call ResolutionStringBuilder 扩展追加调用解析
【发布时间】:2017-01-02 00:15:17
【问题描述】:

我有一个扩展方法,可以将 IEnumerable 中的每个对象附加到 StringBuilder。但是,它使用类型参数在 IEnumerable 上运行,所以我的问题是它会触发单独类型独有的 Append() 调用,还是 everything 作为“对象”通过并且效率低下?如果需要,我将针对 StringBuilder 具有该类型特有的 Append() 调用的特殊情况扩展该函数,但我需要知道是否必须这样做。

    /// <summary>
    /// Appends each object in the enumerable to the StringBuilder's content.
    /// </summary>
    /// <typeparam name="T"></typeparam>
    /// <param name="sb"></param>
    /// <param name="e"></param>
    /// <returns></returns>
    /// <exception cref="System.ArgumentOutOfRangeException"/>
    public static StringBuilder AppendEach<T>(this StringBuilder sb, IEnumerable<T> e)
    {
        e.Do<T>(t => sb.Append(t));
        return sb;
    }

    /// <summary>
    /// Allows you to run code on each value in a collection in-line.
    /// </summary>
    /// <typeparam name="T"></typeparam>
    /// <param name="t"></param>
    /// <param name="action"></param>
    public static void Do<T>(this IEnumerable<T> e, Action<T> action)
    {
        foreach (T t in e) action(t);
    }

不确定我使用的 Do 扩展是否会改变问题的结果,因此我将其包含在内,而不是简化代码。我知道有些人会因为我在这里写“具有副作用的 LINQ 方法”而绞死我,但这与手头的问题无关。

【问题讨论】:

    标签: c# generics ienumerable extension-methods stringbuilder


    【解决方案1】:

    不,它会调用 Append(Object),如果你的意思是“它会触发单独类型独有的 Append() 调用”。

    因为碰巧发生了争执。 C# 编译器必须在编译时决定使用哪种方法。这意味着选择的方法参数不能与您放入 Append(...) 的变量冲突。由于编译器在编译时不知道 T 是什么,它会选择 Append(object),因为它是唯一可以工作的 T 将是什么。

    我已经模仿了这个场景:

    public class C
    {
    public static void Show(object value)
    {
        "object".Dump();
    }
    
    public static void Show(int value)
    {
        "int".Dump();
    }
    
    public static void Show(string value)
    {
        "string".Dump();
    }
    }
    
    public class MyClass<T>
    {
       public void Append(T value)
       {
    C.Show(value);
       }
    }
    
    public void Main()
    {
    var c1 = new MyClass<int>();
    var c2 = new MyClass<string>();
    var c3 = new MyClass<object>();
    
    c1.Append(1);
    c2.Append("1");
    c3.Append(2);
    }
    

    它显示“对象”3次。

    顺便说一句。如果从类中删除对象方法,它将无法编译,因为它无法找到合适的方法。

    【讨论】:

    • 这不太适合他的情况——看我的回答。
    • @Servy 在对另一个答案的评论中很好地阐明了原因:“该方法不是动态绑定的”,“泛型是一个编译时概念。”我为 StringBuilder 支持的每种类型添加了重载,并将原始作为后备并处理对象类型。
    【解决方案2】:

    为了讨论而将其留在这里,除非有人认为适合删除它——但这里的信息不正确。

    所有内容都不会作为“对象”通过——所有内容都将作为 T 恰好是(或 StringBuidler 调用 Append() 的最具体的 T 超类)通过。 因此,如果您传入一个 IEnumerable,则该可枚举中的每个项目都将传递给 Append(object) 覆盖,因为编译器不知道足以调用更具体的覆盖。 然而,如果你传入一个 IEnumerable,那么 Append(string) 将被调用,因为编译器现在对类型有足够的了解来选择正确的覆盖。 如果你传入一个 IEnumerable 但枚举中的每个对象都是一个字符串,它仍然会调用 Append(object) 覆盖,因为类型信息丢失了。

    【讨论】:

    • 好吧,这就是我最初写它时的想法。由于似乎存在一些分歧,我会再等几分钟,然后再选择这个作为让其他人参与进来的答案。
    • 方法不是动态绑定的。它不这样做。泛型是一个编译时概念。
    • @Yushatak 这根本不是它的作用。完全错了。
    • @Servy 也许您可以阐明它是如何运作的以及为什么在答案中这是错误的,以便我可以选择它?
    • @Servy 当你说“泛型是一个编译时概念”时应该理解,我知道但没有正确考虑。一切都说得通,并通过测试控制台应用程序确认您是正确的。
    猜你喜欢
    • 1970-01-01
    • 2018-06-02
    • 1970-01-01
    • 2012-11-24
    • 2010-12-15
    • 1970-01-01
    • 1970-01-01
    • 1970-01-01
    • 2013-01-04
    相关资源
    最近更新 更多