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) 重写,因为类型信息丢失了。
我有一个扩展方法,可以将 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) 重写,因为类型信息丢失了。