StringBuilder 扩展追加调用解析

StringBuilder Extension Append Call Resolution

我有一个扩展方法,可以将 IEnumerable 中的每个对象附加到 StringBuilder。不过,它使用类型参数在 IEnumerable 上运行,所以我的问题是它会触发 Append() 对单独类型唯一的调用,还是 everything 作为 "object" 传递并成为效率低下?如果需要,我将针对特殊情况扩展函数,其中 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 methods with side effects" 而被吊死,但这与手头的问题无关。

不,如果您的意思是 "will it trigger the Append() calls unique to separate types",它会调用 Append(Object)

因为正好发生了争执。 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);
}

它显示"object" 3 次。

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

除非有人认为适合将其删除,否则将其留在这里是为了讨论,但此处的信息不正确。

一切都不会像 "object" 那样传递——一切都会像 T 恰好是的那样传递(或者 StringBuidler 有一个 Append() 调用的 T 的最具体的超类)。 因此,如果您传入 IEnumerable,则该可枚举中的每个项目都将传递给 Append(object) 重写,因为编译器并不知道足够多的信息来调用更具体的重写。 但是,如果您传入一个 IEnumerable,则会调用 Append(string),因为编译器现在对类型了解得足够多,可以 select 进行适当的覆盖。 如果你传入一个 IEnumerable 但枚举中的每个对象都是一个字符串,它仍然会调用 Append(object) 重写,因为类型信息丢失了。